pax_global_header00006660000000000000000000000064137202763770014530gustar00rootroot0000000000000052 comment=d4ad350291f2937c0b6a3eea9e1d0c8e1051fc32 librepo-1.12.1/000077500000000000000000000000001372027637700132465ustar00rootroot00000000000000librepo-1.12.1/.git-commit-template000066400000000000000000000006521372027637700171340ustar00rootroot00000000000000 # In addition to regular commit message, you can uncomment and fill in the # following to include this change in the released RPM package changelog: # = changelog = # msg: # type: # resolves: # related: # msg = message to be included in the changelog # type = one of: bugfix/enhancement/security # resolves = URLs to bugs or issues resolved by this commit # related = URLs to any related bugs or issues librepo-1.12.1/.gitignore000066400000000000000000000001731372027637700152370ustar00rootroot00000000000000build/ TAGS tags librepo/version.h *.pyc doc/c/Doxyfile.in librepo-*.tar.xz librepo-*.rpm python-librepo-*.rpm BUG* ISSUE* librepo-1.12.1/.tito/000077500000000000000000000000001372027637700143035ustar00rootroot00000000000000librepo-1.12.1/.tito/packages/000077500000000000000000000000001372027637700160615ustar00rootroot00000000000000librepo-1.12.1/.tito/packages/.readme000066400000000000000000000002371372027637700173210ustar00rootroot00000000000000the .tito/packages directory contains metadata files named after their packages. Each file has the latest tagged version and the project's relative directory. librepo-1.12.1/.tito/tito.props000066400000000000000000000002231372027637700163440ustar00rootroot00000000000000[buildconfig] builder = tito.builder.Builder tagger = tito.tagger.VersionTagger changelog_do_not_remove_cherrypick = 0 changelog_format = %s (%ae) librepo-1.12.1/.valgrindrc000066400000000000000000000000721372027637700154010ustar00rootroot00000000000000--memcheck:leak-check=full --suppressions=./valgrind.supp librepo-1.12.1/AUTHORS000066400000000000000000000011771372027637700143240ustar00rootroot00000000000000Tomas Mlcoch Colin Walters Richard Hughes Jan Silhan Yevhen Kyriukha yavor-atanasov Paul Clifford Giuseppe Scrivano Radek Holy Ville Skyttä Philippe Ombredanne Alexander Todorov Alan Jenkins Patrick Uiterwijk Neal Gompa Martin Hatina Jaroslav Rohel librepo-1.12.1/CMakeLists.txt000066400000000000000000000050441372027637700160110ustar00rootroot00000000000000PROJECT (librepo C) CMAKE_MINIMUM_REQUIRED (VERSION 2.8.5) OPTION (ENABLE_TESTS "Build test?" ON) OPTION (ENABLE_DOCS "Build docs?" ON) OPTION (WITH_ZCHUNK "Build with zchunk support" ON) INCLUDE (${CMAKE_SOURCE_DIR}/VERSION.cmake) SET (VERSION "${LIBREPO_MAJOR}.${LIBREPO_MINOR}.${LIBREPO_PATCH}") SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall") SET (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -ggdb -O0 -Wall -Wextra -DDEBUG") IF(CMAKE_BUILD_TYPE AND (CMAKE_BUILD_TYPE STREQUAL "DEBUG" OR CMAKE_BUILD_TYPE STREQUAL "debug")) MESSAGE("Build type is set do DEBUG!") ENDIF() # Set librepo log domain add_definitions( -DG_LOG_DOMAIN="librepo" ) # Add path with own cmake modules INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") # Find necessare libraries FIND_PACKAGE(PkgConfig) PKG_CHECK_MODULES(GLIB2 glib-2.0 REQUIRED) PKG_SEARCH_MODULE(LIBCRYPTO REQUIRED libcrypto openssl) PKG_CHECK_MODULES(LIBXML2 libxml-2.0 REQUIRED) FIND_PACKAGE(CURL REQUIRED) FIND_PACKAGE(Gpgme REQUIRED) IF (WITH_ZCHUNK) PKG_CHECK_MODULES(ZCHUNKLIB zck>=0.9.11 REQUIRED) SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWITH_ZCHUNK") SET (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DWITH_ZCHUNK") ENDIF (WITH_ZCHUNK) INCLUDE_DIRECTORIES(${GLIB2_INCLUDE_DIRS}) # Enable large file support ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE) # Check libraries IF (NOT LIBXML2_FOUND) MESSAGE(FATAL_ERROR "No libxml-2.0 library installed") ENDIF (NOT LIBXML2_FOUND) IF (NOT CURL_FOUND) MESSAGE(FATAL_ERROR "No CURL library installed") ENDIF (NOT CURL_FOUND) # Add include dirs INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR}) #INCLUDE_DIRECTORIES(${CHECK_INCLUDE_DIR}) include (GNUInstallDirs) # Python stuff IF (NOT PYTHON_DESIRED) FIND_PACKAGE (PythonInterp REQUIRED) ELSEIF (${PYTHON_DESIRED} STREQUAL "2") FIND_PACKAGE (PythonInterp 2 EXACT REQUIRED) ELSEIF (${PYTHON_DESIRED} STREQUAL "3") FIND_PACKAGE (PythonInterp 3 EXACT REQUIRED) ELSEIF (EXISTS ${PYTHON_DESIRED}) SET (PYTHON_EXECUTABLE ${PYTHON_DESIRED}) FIND_PACKAGE (PythonInterp REQUIRED) ELSE () MESSAGE (FATAL_ERROR "Invalid PYTHON_DESIRED value: " ${PYTHON_DESIRED}) ENDIF() FIND_PACKAGE (PythonLibs REQUIRED) # Subdirs ADD_SUBDIRECTORY (librepo) IF (ENABLE_TESTS) FIND_LIBRARY(CHECK_LIBRARY NAMES check) ENABLE_TESTING() ADD_SUBDIRECTORY (tests) ENDIF (ENABLE_TESTS) IF (ENABLE_DOCS) ADD_SUBDIRECTORY (doc) ENDIF (ENABLE_DOCS) librepo-1.12.1/COPYING000066400000000000000000000636421372027637700143140ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. 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 not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the 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 specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 2.1 of the License, or (at your option) any later version. This library 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! librepo-1.12.1/README.md000066400000000000000000000124101372027637700145230ustar00rootroot00000000000000# librepo librepo - A library providing C and Python (libcURL like) API for downloading linux repository metadata and packages ## Building ### Build requires: Fedora/Ubuntu name * check (http://check.sourceforge.net/) - check-devel/check * cmake (http://www.cmake.org/) - cmake/cmake * expat (http://expat.sourceforge.net/) - expat-devel/libexpat1-dev * gcc (http://gcc.gnu.org/) - gcc/gcc * glib2 (http://developer.gnome.org/glib/) - glib2-devel/libglib2.0-dev * gpgme (http://www.gnupg.org/) - gpgme-devel/libgpgme11-dev * libattr (http://www.bestbits.at/acl/) - libattr-devel/libattr1-dev * libcurl (http://curl.haxx.se/libcurl/) - libcurl-devel/libcurl4-openssl-dev * openssl (http://www.openssl.org/) - openssl-devel/libssl-dev * python (http://python.org/) - python2-devel/libpython2.7-dev (python3-devel/libpython3-dev) * **Test requires:** pygpgme (https://pypi.python.org/pypi/pygpgme/0.1) - pygpgme/python-gpgme (python3-pygpgme/python3-gpgme) * **Test requires:** python-flask (http://flask.pocoo.org/) - python-flask/python-flask * **Test requires:** python-nose (https://nose.readthedocs.org/) - python-nose/python-nose (python3-nose) * **Test requires:** pyxattr (https://github.com/xattr/xattr) - /pyxattr (python3-pyxattr) ### Build from your checkout dir (Python 2 bindings): mkdir build cd build/ cmake .. make ### Build from your checkout dir (Python 3 bindings): mkdir build cd build/ cmake -DPYTHON_DESIRED="3" .. make ### Build with debug flags: mkdir build cd build/ cmake -DCMAKE_BUILD_TYPE="DEBUG" .. make ## Documentation ### Build: cd build/ make doc * C documentation: `build/doc/c/html/index.html` * Python documentation: `build/doc/python/index.html` ### Online python bindings documentation: https://rpm-software-management.github.com/librepo/ ## Python 3 notes: Support for Python 3 is experimental. Python unittests are not fully working due to missing python3 support in Flask module. Flask python3 support rely on python3 support in: * [Werkzeug module](http://werkzeug.pocoo.org/docs/python3/) (Currently - 13.8.2013 - Highly experimental) * [Jinja2 module](http://jinja.pocoo.org/docs/intro/) (Done) * [itsdangerous module](https://github.com/mitsuhiko/itsdangerous) (Done) ## Testing All unit tests run from librepo checkout dir ### Run both (C & Python) tests via makefile: make test ### Run (from your checkout dir) - C unittests: build/tests/test_main tests/test_data/ Available params: * ``-v`` Run tests verbosely (Show Librepo debug messages) * ``-d`` Run download tests (This tests need internet connection) To check memoryleaks: Add this line to your ``~/.bashrc`` file alias gvalgrind='G_SLICE=always-malloc G_DEBUG=gc-friendly valgrind' And then run: CK_FORK=no gvalgrind --leak-check=full build/tests/test_main tests/test_data/ Suppress known still_reachable memory: CK_FORK=no gvalgrind --leak-check=full --suppressions=still_reachable.supp build/tests/test_main tests/test_data/ Note: .valgrindrc file is present in checkoutdir, this file contains the settings: `--memcheck:leak-check=full --suppressions=./valgrind.supp` ### Run (from your checkout dir) - Python 2 unittests: PYTHONPATH=`readlink -f ./build/librepo/python/python2/` nosetests -s -v tests/python/tests/ Example of run only one specific test: ``PYTHONPATH=`readlink -f ./build/librepo/python/python2/` nosetests -s -v tests/python/tests/test_yum_repo_downloading.py:TestCaseYumRepoDownloading.test_download_and_update_repo_01`` ### Run (from your checkout dir) - Python 3 unittests: PYTHONPATH=`readlink -f ./build/librepo/python/python3/` nosetests-3.3 -s -v tests/python/tests/ ## Contribution Here's the most direct way to get your work merged into the project. 1. Fork the project 1. Clone down your fork 1. Implement your feature or bug fix and commit changes 1. If the change fixes a bug at [Red Hat bugzilla](https://bugzilla.redhat.com/), or if it is important to the end user, add the following block to the commit message: = changelog = msg: message to be included in the changelog type: one of: bugfix/enhancement/security (this field is required when message is present) resolves: URLs to bugs or issues resolved by this commit (can be specified multiple times) related: URLs to any related bugs or issues (can be specified multiple times) * For example:: = changelog = msg: Decode package URL when using for local filename type: bugfix resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1817130 * For your convenience, you can also use git commit template by running the following command in the top-level directory of this project: git config commit.template ./.git-commit-template 1. In a separate commit, add your name into the [authors file](https://github.com/rpm-software-management/librepo/blob/master/AUTHORS) as a reward for your generosity 1. Push the branch to your fork 1. Send a pull request for your branch ## Links * [Red Hat Bugzilla](https://bugzilla.redhat.com/buglist.cgi?query_format=advanced&bug_status=NEW&bug_status=ASSIGNED&bug_status=MODIFIED&bug_status=VERIFIED&component=librepo) * [Fedora spec file](http://pkgs.fedoraproject.org/cgit/librepo.git/tree/librepo.spec) librepo-1.12.1/VERSION.cmake000066400000000000000000000001061372027637700153720ustar00rootroot00000000000000SET(LIBREPO_MAJOR "1") SET(LIBREPO_MINOR "12") SET(LIBREPO_PATCH "1") librepo-1.12.1/acceptancetests/000077500000000000000000000000001372027637700164175ustar00rootroot00000000000000librepo-1.12.1/acceptancetests/features/000077500000000000000000000000001372027637700202355ustar00rootroot00000000000000librepo-1.12.1/acceptancetests/features/fastestmirror_with_cache.feature000066400000000000000000000010321372027637700266700ustar00rootroot00000000000000Feature: Package download with fastestmirror and its cache enabled Check that download with fastestmirror and existing cache used is faster then download with fastestmirror without cache. Scenario: Fastest mirror enabled and cache doesn't exist Given metalinkurl: https://mirrors.fedoraproject.org/metalink?repo=fedora-20&arch=x86_64 And Want to download location: Packages/l/librepo-1.2.1-2.fc20.x86_64.rpm When I download the package three times Then Third download with fastestmirrorcache should be the fastest librepo-1.12.1/acceptancetests/features/fastestmirror_with_cache.py000077500000000000000000000050311372027637700256730ustar00rootroot00000000000000import os import os.path import time import librepo import sys from lettuce import * CACHE = "fastestmirror.cache" def remove_fn(fn): if os.path.exists(fn): os.remove(fn) @before.each_feature def feature_setup(feature): world.handle = None world.handle_options = {} world.packages = [] world.filenames = [] @step(r'(\w+): (\S+)') def handle_set_str(step, option, val): world.handle_options[option] = val @step(r'Want to download location: (\S+)') def package_to_download(step, path): world.filenames.append(os.path.basename(path)) world.packages.append(path) @step('I download the package three times') def download_package(step): remove_fn(CACHE) for filename in world.filenames: remove_fn(filename) h = librepo.Handle() h.repotype = librepo.YUMREPO for opt, val in world.handle_options.iteritems(): setattr(h, opt, val) # Download the package for the first time packages = [] for package in world.packages: packages.append(librepo.PackageTarget(package, handle=h)) t = time.time() librepo.download_packages(packages, failfast=True) world.t1 = time.time() - t for package in packages: assert (package.err is None) assert not os.path.exists(CACHE) for filename in world.filenames: assert os.path.exists(filename) remove_fn(filename) # Download the package for the second time h.fastestmirror = True h.fastestmirrorcache = CACHE packages = [] for package in world.packages: packages.append(librepo.PackageTarget(package, handle=h)) t = time.time() librepo.download_packages(packages) world.t2 = time.time() - t for package in packages: assert (package.err is None) assert os.path.exists(CACHE) for filename in world.filenames: assert os.path.exists(filename) remove_fn(filename) # Third download of the package h.fastestmirror = True h.fastestmirrorcache = CACHE packages = [] for package in world.packages: packages.append(librepo.PackageTarget(package, handle=h)) t = time.time() librepo.download_packages(packages) world.t3 = time.time() - t for package in packages: assert (package.err is None) assert os.path.exists(CACHE) for filename in world.filenames: assert os.path.exists(filename) remove_fn(filename) @step('Third download with fastestmirrorcache should be the fastest') def check_results(step): assert world.t2 > world.t3, "Download without cache is faster" librepo-1.12.1/acceptancetests/run.sh000077500000000000000000000001331372027637700175570ustar00rootroot00000000000000#!/bin/bash pushd `dirname $0` PYTHONPATH="../build/librepo/python/python2/" lettuce popd librepo-1.12.1/cmake/000077500000000000000000000000001372027637700143265ustar00rootroot00000000000000librepo-1.12.1/cmake/Modules/000077500000000000000000000000001372027637700157365ustar00rootroot00000000000000librepo-1.12.1/cmake/Modules/FindGpgme.cmake000066400000000000000000000336221372027637700206060ustar00rootroot00000000000000# Code is taken from KDE project: # http://code.metager.de/source/xref/kde/kdepimlibs/cmake/modules/FindGpgme.cmake # # - Try to find the gpgme library # # Algorithm: # - Windows: # On Windows, there's three gpgme variants: gpgme{,-glib,-qt}. # - The variant used determines the event loop integration possible: # - gpgme: no event loop integration possible, only synchronous operations supported # - gpgme-glib: glib event loop integration possible, only asynchronous operations supported # - gpgme-qt: qt event loop integration possible, only asynchronous operations supported # - GPGME_{VANILLA,GLIB,QT}_{FOUND,LIBRARIES} will be set for each of the above # - GPGME_INCLUDES is the same for all of the above # - GPGME_FOUND is set if any of the above was found # - *nix: # There's also three variants: gpgme{,-pthread,-pth}. # - The variant used determines the multithreaded use possible: # - gpgme: no multithreading support available # - gpgme-pthread: multithreading available using POSIX threads # - gpgme-pth: multithreading available using GNU PTH (cooperative multithreading) # - GPGME_{VANILLA,PTH,PTHREAD}_{FOUND,LIBRARIES} will be set for each of the above # - GPGME_INCLUDES is the same for all of the above # - GPGME_FOUND is set if any of the above was found # # GPGME_LIBRARY_DIR - the directory where the libraries are located # # THIS IS ALMOST A 1:1 COPY OF FindAssuan.cmake in kdepim. # Any changes here likely apply there, too. # # do away with crappy condition repetition on else/endfoo set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS_gpgme_saved ${CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS} ) set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true ) #if this is built-in, please replace, if it isn't, export into a MacroToBool.cmake of it's own macro( macro_bool_to_bool FOUND_VAR ) foreach( _current_VAR ${ARGN} ) if ( ${FOUND_VAR} ) set( ${_current_VAR} TRUE ) else() set( ${_current_VAR} FALSE ) endif() endforeach() endmacro() #include (MacroEnsureVersion) if ( WIN32 ) # On Windows, we don't have a gpgme-config script, so we need to # look for the stuff ourselves: # in cmake, AND and OR have the same precedence, there's no # subexpressions, and expressions are evaluated short-circuit'ed # IOW: CMake if() suxx. # Starting with CMake 2.6.3 you can group if expressions with (), but we # don't require 2.6.3 but 2.6.2, we can't use it. Alex set( _seem_to_have_cached_gpgme false ) if ( GPGME_INCLUDES ) if ( GPGME_VANILLA_LIBRARIES OR GPGME_QT_LIBRARIES OR GPGME_GLIB_LIBRARIES ) set( _seem_to_have_cached_gpgme true ) endif() endif() if ( _seem_to_have_cached_gpgme ) macro_bool_to_bool( GPGME_VANILLA_LIBRARIES GPGME_VANILLA_FOUND ) macro_bool_to_bool( GPGME_GLIB_LIBRARIES GPGME_GLIB_FOUND ) macro_bool_to_bool( GPGME_QT_LIBRARIES GPGME_QT_FOUND ) # this would have been preferred: #set( GPGME_*_FOUND macro_bool_to_bool(GPGME_*_LIBRARIES) ) if ( GPGME_VANILLA_FOUND OR GPGME_GLIB_FOUND OR GPGME_QT_FOUND ) set( GPGME_FOUND true ) else() set( GPGME_FOUND false ) endif() else() # is this needed, of just unreflected cut'n'paste? # this isn't a KDE library, after all! if( NOT KDEWIN_FOUND ) find_package( KDEWIN REQUIRED ) endif() set( GPGME_FOUND false ) set( GPGME_VANILLA_FOUND false ) set( GPGME_GLIB_FOUND false ) set( GPGME_QT_FOUND false ) find_path( GPGME_INCLUDES gpgme.h ${CMAKE_INCLUDE_PATH} ${CMAKE_INSTALL_PREFIX}/include ) if (NOT WINCE) find_library( _gpgme_vanilla_library NAMES gpgme libgpgme gpgme-11 libgpgme-11 PATHS ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) else (NOT WINCE) find_library( _gpgme_vanilla_library NAMES libgpgme-11-msc PATHS ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) endif() find_library( _gpgme_glib_library NAMES gpgme-glib libgpgme-glib gpgme-glib-11 libgpgme-glib-11 PATHS ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) find_library( _gpgme_qt_library NAMES gpgme-qt libgpgme-qt gpgme-qt-11 libgpgme-qt-11 PATHS ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) if ( WINCE ) set( _gpg_error_library ) else() find_library( _gpg_error_library NAMES gpg-error libgpg-error gpg-error-0 libgpg-error-0 PATHS ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) endif() set( GPGME_INCLUDES ${GPGME_INCLUDES} ) if ( _gpgme_vanilla_library AND ( _gpg_error_library OR WINCE ) ) set( GPGME_VANILLA_LIBRARIES ${_gpgme_vanilla_library} ${_gpg_error_library} ) set( GPGME_VANILLA_FOUND true ) set( GPGME_FOUND true ) endif() if ( _gpgme_glib_library AND ( _gpg_error_library OR WINCE ) ) set( GPGME_GLIB_LIBRARIES ${_gpgme_glib_library} ${_gpg_error_library} ) set( GPGME_GLIB_FOUND true ) set( GPGME_FOUND true ) endif() if ( _gpgme_qt_library AND ( _gpg_error_library OR WINCE ) ) set( GPGME_QT_LIBRARIES ${_gpgme_qt_library} ${_gpg_error_library} ) set( GPGME_QT_FOUND true ) set( GPGME_FOUND true ) endif() endif() # these are Unix-only: set( GPGME_PTHREAD_FOUND false ) set( GPGME_PTH_FOUND false ) set( HAVE_GPGME_PTHREAD 0 ) set( HAVE_GPGME_PTH 0 ) macro_bool_to_01( GPGME_FOUND HAVE_GPGME ) macro_bool_to_01( GPGME_VANILLA_FOUND HAVE_GPGME_VANILLA ) macro_bool_to_01( GPGME_GLIB_FOUND HAVE_GPGME_GLIB ) macro_bool_to_01( GPGME_QT_FOUND HAVE_GPGME_QT ) else() # not WIN32 # On *nix, we have the gpgme-config script which can tell us all we # need to know: # see WIN32 case for an explanation of what this does: set( _seem_to_have_cached_gpgme false ) if ( GPGME_INCLUDES ) if ( GPGME_VANILLA_LIBRARIES OR GPGME_PTHREAD_LIBRARIES OR GPGME_PTH_LIBRARIES ) set( _seem_to_have_cached_gpgme true ) endif() endif() if ( _seem_to_have_cached_gpgme ) macro_bool_to_bool( GPGME_VANILLA_LIBRARIES GPGME_VANILLA_FOUND ) macro_bool_to_bool( GPGME_PTHREAD_LIBRARIES GPGME_PTHREAD_FOUND ) macro_bool_to_bool( GPGME_PTH_LIBRARIES GPGME_PTH_FOUND ) if ( GPGME_VANILLA_FOUND OR GPGME_PTHREAD_FOUND OR GPGME_PTH_FOUND ) set( GPGME_FOUND true ) else() set( GPGME_FOUND false ) endif() else() set( GPGME_FOUND false ) set( GPGME_VANILLA_FOUND false ) set( GPGME_PTHREAD_FOUND false ) set( GPGME_PTH_FOUND false ) find_program( _GPGMECONFIG_EXECUTABLE NAMES gpgme-config ) # if gpgme-config has been found if ( _GPGMECONFIG_EXECUTABLE ) message( STATUS "Found gpgme-config at ${_GPGMECONFIG_EXECUTABLE}" ) exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --version OUTPUT_VARIABLE GPGME_VERSION ) # set( _GPGME_MIN_VERSION "1.1.7" ) # macro_ensure_version( ${_GPGME_MIN_VERSION} ${GPGME_VERSION} _GPGME_INSTALLED_VERSION_OK ) # if ( NOT _GPGME_INSTALLED_VERSION_OK ) # message( STATUS "The installed version of gpgme is too old: ${GPGME_VERSION} (required: >= ${_GPGME_MIN_VERSION})" ) # else() message( STATUS "Found gpgme v${GPGME_VERSION}, checking for flavours..." ) exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --libs OUTPUT_VARIABLE _gpgme_config_vanilla_libs RETURN_VALUE _ret ) if ( _ret ) set( _gpgme_config_vanilla_libs ) endif() exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --thread=pthread --libs OUTPUT_VARIABLE _gpgme_config_pthread_libs RETURN_VALUE _ret ) if ( _ret ) set( _gpgme_config_pthread_libs ) endif() exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --thread=pth --libs OUTPUT_VARIABLE _gpgme_config_pth_libs RETURN_VALUE _ret ) if ( _ret ) set( _gpgme_config_pth_libs ) endif() # append -lgpg-error to the list of libraries, if necessary foreach ( _flavour vanilla pthread pth ) if ( _gpgme_config_${_flavour}_libs AND NOT _gpgme_config_${_flavour}_libs MATCHES "lgpg-error" ) set( _gpgme_config_${_flavour}_libs "${_gpgme_config_${_flavour}_libs} -lgpg-error" ) endif() endforeach() if ( _gpgme_config_vanilla_libs OR _gpgme_config_pthread_libs OR _gpgme_config_pth_libs ) exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --cflags OUTPUT_VARIABLE _GPGME_CFLAGS ) if ( _GPGME_CFLAGS ) string( REGEX REPLACE "(\r?\n)+$" " " _GPGME_CFLAGS "${_GPGME_CFLAGS}" ) string( REGEX REPLACE " *-I" ";" GPGME_INCLUDES "${_GPGME_CFLAGS}" ) endif() foreach ( _flavour vanilla pthread pth ) if ( _gpgme_config_${_flavour}_libs ) set( _gpgme_library_dirs ) set( _gpgme_library_names ) string( TOUPPER "${_flavour}" _FLAVOUR ) string( REGEX REPLACE " +" ";" _gpgme_config_${_flavour}_libs "${_gpgme_config_${_flavour}_libs}" ) foreach( _flag ${_gpgme_config_${_flavour}_libs} ) if ( "${_flag}" MATCHES "^-L" ) string( REGEX REPLACE "^-L" "" _dir "${_flag}" ) file( TO_CMAKE_PATH "${_dir}" _dir ) set( _gpgme_library_dirs ${_gpgme_library_dirs} "${_dir}" ) elseif( "${_flag}" MATCHES "^-l" ) string( REGEX REPLACE "^-l" "" _name "${_flag}" ) set( _gpgme_library_names ${_gpgme_library_names} "${_name}" ) endif() endforeach() set( GPGME_${_FLAVOUR}_FOUND true ) foreach( _name ${_gpgme_library_names} ) set( _gpgme_${_name}_lib ) # if -L options were given, look only there if ( _gpgme_library_dirs ) find_library( _gpgme_${_name}_lib NAMES ${_name} PATHS ${_gpgme_library_dirs} NO_DEFAULT_PATH ) endif() # if not found there, look in system directories if ( NOT _gpgme_${_name}_lib ) find_library( _gpgme_${_name}_lib NAMES ${_name} ) endif() # if still not found, then the whole flavour isn't found if ( NOT _gpgme_${_name}_lib ) if ( GPGME_${_FLAVOUR}_FOUND ) set( GPGME_${_FLAVOUR}_FOUND false ) set( _not_found_reason "dependent library ${_name} wasn't found" ) endif() endif() set( GPGME_${_FLAVOUR}_LIBRARIES ${GPGME_${_FLAVOUR}_LIBRARIES} "${_gpgme_${_name}_lib}" ) endforeach() #check_c_library_exists_explicit( gpgme gpgme_check_version "${_GPGME_CFLAGS}" "${GPGME_LIBRARIES}" GPGME_FOUND ) if ( GPGME_${_FLAVOUR}_FOUND ) message( STATUS " Found flavour '${_flavour}', checking whether it's usable...yes" ) else() message( STATUS " Found flavour '${_flavour}', checking whether it's usable...no" ) message( STATUS " (${_not_found_reason})" ) endif() endif() endforeach( _flavour ) # ensure that they are cached # This comment above doesn't make sense, the four following lines seem to do nothing. Alex set( GPGME_INCLUDES ${GPGME_INCLUDES} ) set( GPGME_VANILLA_LIBRARIES ${GPGME_VANILLA_LIBRARIES} ) set( GPGME_PTHREAD_LIBRARIES ${GPGME_PTHREAD_LIBRARIES} ) set( GPGME_PTH_LIBRARIES ${GPGME_PTH_LIBRARIES} ) if ( GPGME_VANILLA_FOUND OR GPGME_PTHREAD_FOUND OR GPGME_PTH_FOUND ) set( GPGME_FOUND true ) else() set( GPGME_FOUND false ) endif() # endif() endif() endif() endif() # these are Windows-only: set( GPGME_GLIB_FOUND false ) set( GPGME_QT_FOUND false ) set( HAVE_GPGME_GLIB 0 ) set( HAVE_GPGME_QT 0 ) # macro_bool_to_01( GPGME_FOUND HAVE_GPGME ) # macro_bool_to_01( GPGME_VANILLA_FOUND HAVE_GPGME_VANILLA ) # macro_bool_to_01( GPGME_PTHREAD_FOUND HAVE_GPGME_PTHREAD ) # macro_bool_to_01( GPGME_PTH_FOUND HAVE_GPGME_PTH ) endif() # WIN32 | Unix set( _gpgme_flavours "" ) if ( GPGME_VANILLA_FOUND ) set( _gpgme_flavours "${_gpgme_flavours} vanilla" ) endif() if ( GPGME_GLIB_FOUND ) set( _gpgme_flavours "${_gpgme_flavours} Glib" ) endif() if ( GPGME_QT_FOUND ) set( _gpgme_flavours "${_gpgme_flavours} Qt" ) endif() if ( GPGME_PTHREAD_FOUND ) set( _gpgme_flavours "${_gpgme_flavours} pthread" ) endif() if ( GPGME_PTH_FOUND ) set( _gpgme_flavours "${_gpgme_flavours} pth" ) endif() # determine the library in one of the found flavours, can be reused e.g. by FindQgpgme.cmake, Alex foreach(_currentFlavour vanilla glib qt pth pthread) if(NOT GPGME_LIBRARY_DIR) get_filename_component(GPGME_LIBRARY_DIR "${_gpgme_${_currentFlavour}_lib}" PATH) endif() endforeach() if ( NOT Gpgme_FIND_QUIETLY ) if ( GPGME_FOUND ) message( STATUS "Usable gpgme flavours found: ${_gpgme_flavours}" ) else() message( STATUS "No usable gpgme flavours found." ) endif() macro_bool_to_bool( Gpgme_FIND_REQUIRED _req ) if ( WIN32 ) set( _gpgme_homepage "http://www.gpg4win.org" ) else() set( _gpgme_homepage "http://www.gnupg.org/related_software/gpgme" ) endif() # macro_log_feature( # GPGME_FOUND # "gpgme" # "GNU Privacy Guard (GPG/PGP) support" # ${_gpgme_homepage} # ${_req} # "${_GPGME_MIN_VERSION} or greater" # "Necessary to compile many PIM applications, including KMail" #) else() if ( Gpgme_FIND_REQUIRED AND NOT GPGME_FOUND ) message( FATAL_ERROR "Did not find GPGME" ) endif() endif() set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS_gpgme_saved ) librepo-1.12.1/doc/000077500000000000000000000000001372027637700140135ustar00rootroot00000000000000librepo-1.12.1/doc/CMakeLists.txt000066400000000000000000000001601372027637700165500ustar00rootroot00000000000000ADD_SUBDIRECTORY (c) ADD_SUBDIRECTORY (python) ADD_CUSTOM_TARGET (doc) ADD_DEPENDENCIES (doc doc-python doc-c) librepo-1.12.1/doc/c/000077500000000000000000000000001372027637700142355ustar00rootroot00000000000000librepo-1.12.1/doc/c/CMakeLists.txt000066400000000000000000000004721372027637700170000ustar00rootroot00000000000000find_package(Doxygen) if(DOXYGEN_FOUND) CONFIGURE_FILE("Doxyfile.in.in" "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in" @ONLY) add_custom_target(doc-c ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in COMMENT "Building C API documentation with Doxygen" VERBATIM) endif(DOXYGEN_FOUND) librepo-1.12.1/doc/c/Doxyfile.in.in000066400000000000000000002277421372027637700167730ustar00rootroot00000000000000# Doxyfile 1.8.1.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = "Librepo library" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "C library for downloading linux repository metadata and packages" # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = YES # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = @CMAKE_SOURCE_DIR@/librepo/checksum.h \ @CMAKE_SOURCE_DIR@/librepo/gpg.h \ @CMAKE_SOURCE_DIR@/librepo/handle.h \ @CMAKE_SOURCE_DIR@/librepo/metalink.h \ @CMAKE_SOURCE_DIR@/librepo/librepo.h \ @CMAKE_SOURCE_DIR@/librepo/package_downloader.h \ @CMAKE_SOURCE_DIR@/librepo/rcodes.h \ @CMAKE_SOURCE_DIR@/librepo/repomd.h \ @CMAKE_SOURCE_DIR@/librepo/repoutil_yum.h \ @CMAKE_SOURCE_DIR@/librepo/result.h \ @CMAKE_SOURCE_DIR@/librepo/types.h \ @CMAKE_SOURCE_DIR@/librepo/url_substitution.h \ @CMAKE_SOURCE_DIR@/librepo/util.h \ @CMAKE_SOURCE_DIR@/librepo/version.h \ @CMAKE_SOURCE_DIR@/librepo/xmlparser.h \ @CMAKE_SOURCE_DIR@/librepo/yum.h # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = *.h # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = *_internal.h # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # style sheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # managable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES librepo-1.12.1/doc/python/000077500000000000000000000000001372027637700153345ustar00rootroot00000000000000librepo-1.12.1/doc/python/CMakeLists.txt000066400000000000000000000004341372027637700200750ustar00rootroot00000000000000ADD_CUSTOM_TARGET (doc-python PYTHONPATH=${CMAKE_BINARY_DIR}/librepo/python/python${PYTHON_VERSION_MAJOR} sphinx-build -E -b html ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Building Python API documentation with Sphinx") librepo-1.12.1/doc/python/conf.py000066400000000000000000000172331372027637700166410ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # librepo documentation build configuration file, created by # sphinx-quickstart on Tue Jan 8 13:45:14 2013. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os import librepo # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'librepo' copyright = u'2013, Red Hat' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = librepo.VERSION # The full version, including alpha/beta/rc tags. release = '%s-1' % version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'librepodoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'librepo.tex', u'librepo Documentation', u'Tomas Mlcoch', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'librepo', u'librepo Documentation', [u'Tomas Mlcoch'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'librepo', u'librepo Documentation', u'Tomas Mlcoch', 'librepo', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # Include __init__'s doc string into documentation autoclass_content = 'both' librepo-1.12.1/doc/python/examples.rst000066400000000000000000000206601372027637700177100ustar00rootroot00000000000000.. _examples: Examples ======== Librepo usage examples. [More examples (including C examples)](https://github.com/Tojaj/librepo/tree/master/examples) Simple download of metadata --------------------------- :: """ Example: Simple download whole repository This example uses more "pythonic" way of usage. Instead of use setopt() method it uses class properties. Use case: We have a metalink url of a repository and we want do download complete repository metadata. """ import librepo # Metalink URL METALINK_URL = "https://mirrors.fedoraproject.org/metalink?repo=fedora-18&arch=x86_64" # Destination directory (note: This directory must exists!) DESTDIR = "downloaded_metadata" if __name__ == "__main__": h = librepo.Handle() r = librepo.Result() # Repository with repodata in the rpm-md format h.repotype = librepo.LR_YUMREPO # Set metalink url h.mirrorlist = METALINK_URL # Destination directory for metadata h.destdir = DESTDIR try: h.perform(r) except librepo.LibrepoException as e: # rc - Return code (integer value) # msg - Detailed error message (string) # general_msg - Error message based on rc (string) rc, msg, general_msg = e print "Error: %s" % msg Metadata localisation --------------------- :: """ Example: Localize metadata files of a local repository Use case: We have a local repositository in the rpm-md format and want to paths to all its metadata files. Repomd content is just a bonus. """ import sys import librepo import pprint METADATA_PATH = "downloaded_metadata" if __name__ == "__main__": h = librepo.Handle() r = librepo.Result() # Repository with repodata in the rpm-md format h.setopt(librepo.LRO_REPOTYPE, librepo.LR_YUMREPO) # Path to metadata h.setopt(librepo.LRO_URLS, [METADATA_PATH]) # Do not duplicate (copy) metadata, just localise them h.setopt(librepo.LRO_LOCAL, True) try: h.perform(r) except librepo.LibrepoException as e: rc, msg, general_msg = e print "Error: %s" % msg sys.exit(1) print "Repomd content:" pprint.pprint(r.getinfo(librepo.LRR_YUM_REPOMD)) print "\nPaths to metadata files:" for data_type, path in r.getinfo(librepo.LRR_YUM_REPO).iteritems(): print "%15s: %s" % (data_type, path) sys.exit(0) Checksum verification --------------------- .. note:: Checksum verification is enabled by default. So command ``h.setopt(librepo.LRO_CHECKSUM, True)`` is unnecessary, but for illustration it is better to write the command anyway. :: """ Example: Verify checksum of local metadata Use case: We have some incomplete metadata localy. They are incomplete because they doesn't contain all files specified in repomd.xml. They contains only primary.xml and filelists.xml. We want to check checksum of this metadata. """ import sys import librepo METADATA_PATH = "downloaded_metadata" if __name__ == "__main__": h = librepo.Handle() r = librepo.Result() # Repository with repodata in the rpm-md format h.setopt(librepo.LRO_REPOTYPE, librepo.LR_YUMREPO) # Path to the metadata h.setopt(librepo.LRO_URLS, [METADATA_PATH]) # Do not duplicate (copy) the metadata h.setopt(librepo.LRO_LOCAL, True) # Check checksum of metadata h.setopt(librepo.LRO_CHECKSUM, True) # Ignore missing metadata files h.setopt(librepo.LRO_IGNOREMISSING, True) try: h.perform(r) except librepo.LibrepoException as e: rc, msg, general_msg = e if rc == librepo.LRE_BADCHECKSUM: print "Corrupted metadata! (%s)" % msg else: print "Other error: %s" % msg sys.exit(1) print "Metadata are fine!" More complex download --------------------- :: """ librepo - example of usage """ import os import sys import shutil from pprint import pprint import librepo DESTDIR = "downloaded_metadata" PROGRESSBAR_LEN = 50 def callback(data, total_to_download, downloaded): """Progress callback""" if total_to_download <= 0: return completed = int(downloaded / (total_to_download / PROGRESSBAR_LEN)) print "[%s%s] %8s/%8s (%s)\r" % ('#'*completed, '-'*(PROGRESSBAR_LEN-completed), int(downloaded), int(total_to_download), data), sys.stdout.flush() if __name__ == "__main__": # Prepare destination directory if os.path.exists(DESTDIR): if not os.path.isdir(DESTDIR): raise IOError("%s is not a directory" % DESTDIR) shutil.rmtree(DESTDIR) os.mkdir(DESTDIR) h = librepo.Handle() # Handle represents a download configuration r = librepo.Result() # Result represents an existing/downloaded repository # --- Mandatory arguments ------------------------------------------- # URL of repository or URL of metalink/mirrorlist h.setopt(librepo.LRO_URLS, ["http://ftp.linux.ncsu.edu/pub/fedora/linux/releases/17/Everything/i386/os/"]) #h.setopt(librepo.LRO_MIRRORLIST, "https://mirrors.fedoraproject.org/metalink?repo=fedora-source-17&arch=i386") # Note: LRO_URLS and LRO_MIRRORLIST could be set and used simultaneously # and if download from LRO_URLS failed, then mirrorlist is used # Type of repository h.setopt(librepo.LRO_REPOTYPE, librepo.LR_YUMREPO) # --- Optional arguments -------------------------------------------- # Make download interruptible h.setopt(librepo.LRO_INTERRUPTIBLE, True) # Destination directory for metadata h.setopt(librepo.LRO_DESTDIR, DESTDIR) # Check checksum of all files (if checksum is available in repomd.xml) h.setopt(librepo.LRO_CHECKSUM, True) # Callback to display progress of downloading h.setopt(librepo.LRO_PROGRESSCB, callback) # Set user data for the callback h.setopt(librepo.LRO_PROGRESSDATA, {'test': 'dict', 'foo': 'bar'}) # Download only filelists.xml, prestodelta.xml # Note: repomd.xml is downloaded implicitly! # Note: If LRO_YUMDLIST is None -> all files are downloaded h.setopt(librepo.LRO_YUMDLIST, ["filelists", "prestodelta"]) h.perform(r) # Get and show results pprint (r.getinfo(librepo.LRR_YUM_REPO)) pprint (r.getinfo(librepo.LRR_YUM_REPOMD)) # Whoops... I forget to download primary.xml.. Lets fix it! # Set LRO_UPDATE - only update existing Result h.setopt(librepo.LRO_UPDATE, True) h.setopt(librepo.LRO_YUMDLIST, ["primary"]) h.perform(r) # List of mirrors # (In this case no mirrorlist is used -> list will contain only one url) # Example of access info via attr insted of .getinfo() method pprint (h.mirrors) # Get and show final results pprint (r.getinfo(librepo.LRR_YUM_REPO)) pprint (r.getinfo(librepo.LRR_YUM_REPOMD)) How to get urls in a local mirrorlist ------------------------------------- :: import os import sys import librepo import pprint DESTDIR = "downloaded_metadata" if __name__ == "__main__": h = librepo.Handle() r = librepo.Result() # Correct repotype is important. Without repotype # metalink parser doesn't know suffix which should # be stripped off from the mirrors urls. h.setopt(librepo.LRO_REPOTYPE, librepo.LR_YUMREPO) # Set local mirrorlist file as mirrorlist if os.path.isfile(os.path.join(DESTDIR, "mirrorlist")): h.mirrorlist = os.path.join(DESTDIR, "mirrorlist") elif os.path.isfile(os.path.join(DESTDIR, "metalink.xml")): h.mirrorlist = os.path.join(DESTDIR, "metalink.xml") else: print "No mirrorlist of downloaded repodata available" sys.exit(0) # Download only the mirrorlist during perform() call. h.setopt(librepo.LRO_FETCHMIRRORS, True) h.perform(r) print "Urls in mirrorlist:" print h.mirrors print "Metalink file content:" pprint.pprint(h.metalink) librepo-1.12.1/doc/python/handle.rst000066400000000000000000000001501372027637700173150ustar00rootroot00000000000000.. _handle: Handle (librepo.Handle) ======================= .. autoclass:: librepo.Handle :members:librepo-1.12.1/doc/python/index.rst000066400000000000000000000004421372027637700171750ustar00rootroot00000000000000####### librepo ####### A library providing C and Python (libcURL like) API for downloading linux repository metadata and packages Contents: .. toctree:: :maxdepth: 2 lib examples Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` librepo-1.12.1/doc/python/lib.rst000066400000000000000000000017711372027637700166420ustar00rootroot00000000000000.. _lib: The Librepo Library =================== Classes ------- Librepo includes several classes: .. toctree:: handle result packagetarget Exceptions ---------- Librepo module has only one own exception. .. class:: LibrepoException Value of this exception is tuple with three elements: ``(return code, error message, general error message)`` * Return code is a value from: :ref:`error-codes-label`. * String with a descriptive description of the error. * General error message based on rc (feel free to ignore this message) Constants ---------- .. automodule:: librepo Functions --------- .. autofunction:: checksum_str_to_type .. autofunction:: download_packages .. autofunction:: download_url .. autofunction:: yum_repomd_get_age Debugging --------- Some tricky/unsafe stuff which can be useful during developing and debugging, but definitely shouldn't be used in production. .. autofunction:: log_set_file .. autofunction:: log_remove_handler .. autofunction:: set_debug_log_handler librepo-1.12.1/doc/python/packagetarget.rst000066400000000000000000000002231372027637700206650ustar00rootroot00000000000000.. _packagetarget: PackageTarget (librepo.PackageTarget) ===================================== .. autoclass:: librepo.PackageTarget :members: librepo-1.12.1/doc/python/result.rst000066400000000000000000000001501372027637700174000ustar00rootroot00000000000000.. _result: Result (librepo.Result) ======================= .. autoclass:: librepo.Result :members:librepo-1.12.1/examples/000077500000000000000000000000001372027637700150645ustar00rootroot00000000000000librepo-1.12.1/examples/c/000077500000000000000000000000001372027637700153065ustar00rootroot00000000000000librepo-1.12.1/examples/c/Makefile000066400000000000000000000023171372027637700167510ustar00rootroot00000000000000 # export LD_LIBRARY_PATH="../../build/librepo/" CC=gcc CFLAGS= -Wall -Wextra -g -std=c99 -O3 -I../../ `pkg-config --cflags glib-2.0` LINKFLAGS= -L../../build/librepo/ -lrepo `pkg-config --libs glib-2.0` all: \ download_repo \ download_low_level_api \ download_packages \ download_repo_with_callback \ fastestmirror \ fastestmirror_with_callback download_repo: $(CC) $(CFLAGS) download_repo.c $(LINKFLAGS) -o download_repo download_low_level_api: $(CC) $(CFLAGS) download_low_level_api.c $(LINKFLAGS) -o download_low_level_api download_packages: $(CC) $(CFLAGS) download_packages.c $(LINKFLAGS) -o download_packages download_repo_with_callback: $(CC) $(CFLAGS) download_repo_with_callback.c $(LINKFLAGS) -o download_repo_with_callback fastestmirror: $(CC) $(CFLAGS) fastestmirror.c $(LINKFLAGS) -o fastestmirror fastestmirror_with_callback: $(CC) $(CFLAGS) fastestmirror_with_callback.c $(LINKFLAGS) -o fastestmirror_with_callback clean: rm -f \ download_repo \ download_low_level_api \ download_packages \ download_repo_with_callback \ fastestmirror \ fastestmirror_with_callback run: LD_LIBRARY_PATH="../../build/librepo/" ./download_repo librepo-1.12.1/examples/c/download_low_level_api.c000066400000000000000000000045651372027637700221740ustar00rootroot00000000000000#include #include #include #include #define URL "http://curl.haxx.se/libcurl/c/curl_easy_setopt.html" static void log_handler_cb(const gchar *log_domain G_GNUC_UNUSED, GLogLevelFlags log_level G_GNUC_UNUSED, const gchar *message, gpointer user_data G_GNUC_UNUSED) { g_print ("%s\n", message); } int main(void) { int rc = EXIT_SUCCESS; gboolean ret; LrDownloadTarget *target; GError *tmp_err = NULL; // Setup logging (optional step) /* g_log_set_handler("librepo", G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_WARNING, log_handler_cb, NULL); */ // Download something GSList *possible_checksums = NULL; /* LrDownloadTargetChecksum *chksm = lr_downloadtargetchecksum_new( LR_CHECKSUM_SHA256, "8d98b9e32651ae881c7d5bf7cf19ccaee3991bf0ca93b2ca58771c9df80bf077"); possible_checksums = g_slist_prepend(possible_checksums, chksm); */ target = lr_downloadtarget_new( NULL, // LrHandle URL, // Whole or relative part of URL NULL, // Base URL use this in case of relative part in the prev argument -1, // Target fd (opened of course). Note: 0 means stdout "downloaded.html", // Target fn possible_checksums, // List of possible checksums 0, // Expected size FALSE, // Resume NULL, // Progress callback NULL, // User data for progress callback NULL, // End callback NULL, // Mirror failure callback NULL, // User's data for the end and mirror failure callbacks 0, // Start of byterange 3788 // End of byterange ); ret = lr_download_target(target, &tmp_err); if (!ret) { fprintf(stderr, "Error encountered: %d: %s\n", tmp_err->code, tmp_err->message); rc = EXIT_FAILURE; g_error_free(tmp_err); } // Check statuses printf("%s: %s\n", target->effectiveurl, target->err ? target->err : "OK"); // Clean up lr_downloadtarget_free(target); return rc; } librepo-1.12.1/examples/c/download_packages.c000066400000000000000000000065631372027637700211310ustar00rootroot00000000000000#include #include #include #include static void log_handler_cb(const gchar *log_domain G_GNUC_UNUSED, GLogLevelFlags log_level G_GNUC_UNUSED, const gchar *message, gpointer user_data G_GNUC_UNUSED) { g_print ("%s\n", message); } int main(void) { int rc = EXIT_SUCCESS; gboolean ret; LrHandle *h; GSList *packages = NULL; LrPackageTarget *target; GError *tmp_err = NULL; // Setup logging (optional step) g_log_set_handler("librepo", G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_WARNING, log_handler_cb, NULL); // Prepare handle char *urls[] = {"http://beaker-project.org/yum/client-testing/Fedora19/", NULL}; h = lr_handle_init(); lr_handle_setopt(h, NULL, LRO_URLS, urls); lr_handle_setopt(h, NULL, LRO_REPOTYPE, LR_YUMREPO); // Prepare list of targets // This target has enabled resume and filled checksum. // So if package would localy exists and checksum matches, then // no download was performed. // Alternatively if you used "truncate -s 1000 beaker-0.14.0-1.fc18.src.rpm" // and truncate the package to 1000 bytes. Then during download, no whole // package will be downloaded, but download will resume from the 1001st byte target = lr_packagetarget_new(h, "beaker-0.14.0-1.fc18.src.rpm", NULL, LR_CHECKSUM_SHA256, "737c974110914a073fb6c736cd7021b0d844c9e47e7d21e37d687dbc86d36538", 0, NULL, TRUE, NULL, NULL, &tmp_err); packages = g_slist_append(packages, target); target = lr_packagetarget_new(h, "beaker-client-0.14.1-1.fc18.noarch.rpm", NULL, LR_CHECKSUM_UNKNOWN, NULL, 325144, NULL, FALSE, NULL, NULL, &tmp_err); packages = g_slist_append(packages, target); target = lr_packagetarget_new(h, "rhts-4.56-1.fc17.src.rpm", NULL, LR_CHECKSUM_UNKNOWN, NULL, 0, NULL, FALSE, NULL, NULL, &tmp_err); packages = g_slist_append(packages, target); // Download all targets // Note that failfast feature is enabled. This means that if any of // downloads will fail, then lr_download_package returns immediately // with an error. // If the failfast is disabled, then lr_download_packages doesn't returns // sooner then all downloads are finished (no matter if successfully // or unsuccessfully) or some critical error is meet. ret = lr_download_packages(packages, LR_PACKAGEDOWNLOAD_FAILFAST, &tmp_err); if (!ret) { fprintf(stderr, "Error encountered: %d: %s\n", tmp_err->code, tmp_err->message); rc = EXIT_FAILURE; g_error_free(tmp_err); } // Check statuses for (GSList *elem = packages; elem; elem = g_slist_next(elem)) { LrPackageTarget *target = elem->data; printf("%s: %s\n", target->local_path, target->err ? target->err : "OK"); } // Clean up g_slist_free_full(packages, (GDestroyNotify) lr_packagetarget_free); lr_handle_free(h); return rc; } librepo-1.12.1/examples/c/download_repo.c000066400000000000000000000046031372027637700203110ustar00rootroot00000000000000#include #include #include #include int main(void) { int rc = EXIT_SUCCESS; gboolean ret; LrHandle *h; LrResult *r; LrYumRepoMd *repomd; char *download_list[] = LR_YUM_HAWKEY; GError *tmp_err = NULL; h = lr_handle_init(); r = lr_result_init(); char *urls[] = {"http://beaker-project.org/yum/client-testing/Fedora19/", NULL}; lr_handle_setopt(h, NULL, LRO_URLS, urls); lr_handle_setopt(h, NULL, LRO_REPOTYPE, LR_YUMREPO); lr_handle_setopt(h, NULL, LRO_YUMDLIST, download_list); ret = lr_handle_perform(h, r, &tmp_err); if (!ret) { fprintf(stderr, "Error encountered: %d: %s\n", tmp_err->code, tmp_err->message); g_error_free(tmp_err); rc = EXIT_FAILURE; } lr_result_getinfo(r, &tmp_err, LRR_YUM_REPOMD, &repomd); if (!tmp_err) { printf("Repomd.xml contains:\n"); for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) { LrYumRepoMdRecord *rec = (LrYumRepoMdRecord *) elem->data; printf(" Type: %s\n", rec->type); printf(" Location href: %s\n", rec->location_href); if (rec->location_base) printf(" Location base: %s\n", rec->location_base); if (rec->checksum) printf(" Checksum: %s\n", rec->checksum); if (rec->checksum_type) printf(" Checksum type: %s\n", rec->checksum_type); if (rec->checksum_open) printf(" Checksum open: %s\n", rec->checksum_open); if (rec->checksum_open_type) printf(" Checksum open type: %s\n", rec->checksum_open_type); if (rec->timestamp > 0) printf(" Timestamp: %"G_GINT64_FORMAT"\n", rec->timestamp); if (rec->size > 0) printf(" Size: %"G_GINT64_FORMAT"\n", rec->size); if (rec->size_open > 0) printf(" Size open: %"G_GINT64_FORMAT"\n", rec->size_open); if (rec->db_version > 0) printf(" Db version: %d\n", rec->db_version); printf("------------------------------------------------\n"); } } else { fprintf(stderr, "Error encountered: %s\n", tmp_err->message); g_error_free(tmp_err); rc = EXIT_FAILURE; } lr_result_free(r); lr_handle_free(h); return rc; } librepo-1.12.1/examples/c/download_repo_with_callback.c000066400000000000000000000043041372027637700231560ustar00rootroot00000000000000#include #include #include #include #define FEDORA_VERSION "19" #define FEDORA_BASEARCH "x86_64" static void log_handler_cb(const gchar *log_domain G_GNUC_UNUSED, GLogLevelFlags log_level G_GNUC_UNUSED, const gchar *message, gpointer user_data G_GNUC_UNUSED) { g_print ("%s\n", message); } static int progress_callback(G_GNUC_UNUSED void *data, double total_to_download, double now_downloaded) { printf("\r%f / %f", total_to_download, now_downloaded); fflush(stdout); return 0; } int main(void) { int rc = EXIT_SUCCESS; GError *tmp_err = NULL; // Setup logging (useful for debugging) //g_log_set_handler("librepo", G_LOG_LEVEL_ERROR | // G_LOG_LEVEL_CRITICAL | // G_LOG_LEVEL_DEBUG | // G_LOG_LEVEL_WARNING, // log_handler_cb, NULL); // Prepare list of variable substitutions LrUrlVars *urlvars = NULL; urlvars = lr_urlvars_set(urlvars, "releasever", FEDORA_VERSION); urlvars = lr_urlvars_set(urlvars, "basearch", FEDORA_BASEARCH); // Download only this metadata char *download_list[] = { "group_gz", "pkgtags", NULL }; LrHandle *h = lr_handle_init(); lr_handle_setopt(h, NULL, LRO_METALINKURL, "https://mirrors.fedoraproject.org/metalink?repo=updates-released-f$releasever&arch=$basearch"); lr_handle_setopt(h, NULL, LRO_REPOTYPE, LR_YUMREPO); lr_handle_setopt(h, NULL, LRO_YUMDLIST, download_list); lr_handle_setopt(h, NULL, LRO_VARSUB, urlvars); lr_handle_setopt(h, NULL, LRO_PROGRESSCB, progress_callback); LrResult *r = lr_result_init(); gboolean ret = lr_handle_perform(h, r, &tmp_err); printf("\n"); char *destdir; lr_handle_getinfo(h, NULL, LRI_DESTDIR, &destdir); if (ret) { printf("Download successful (Destination dir: %s)\n", destdir); } else { fprintf(stderr, "Error encountered: %s\n", tmp_err->message); g_error_free(tmp_err); rc = EXIT_FAILURE; } lr_result_free(r); lr_handle_free(h); return rc; } librepo-1.12.1/examples/c/fastestmirror.c000066400000000000000000000024661372027637700203660ustar00rootroot00000000000000#include #include #include #include static void log_handler_cb(const gchar *log_domain G_GNUC_UNUSED, GLogLevelFlags log_level G_GNUC_UNUSED, const gchar *message, gpointer user_data G_GNUC_UNUSED) { g_print ("%s\n", message); } int main(int argc, char *argv[]) { int rc = EXIT_SUCCESS; GSList *list = NULL; GError *tmp_err = NULL; g_log_set_handler("librepo", G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_WARNING, log_handler_cb, NULL); if (argc < 2) { g_printerr("Usage: %s ...\n", argv[0]); return EXIT_FAILURE; } for (int x = 1; x < argc; x++) list = g_slist_prepend(list, argv[x]); list = g_slist_reverse(list); gboolean ret = lr_fastestmirror(NULL, &list, &tmp_err); if (!ret) { g_printerr("Error encountered: %s\n", tmp_err->message); g_error_free(tmp_err); rc = EXIT_FAILURE; } for (GSList *elem = list; elem; elem = g_slist_next(elem)) { gchar *url = elem->data; g_print("%s\n", url); } g_slist_free(list); return rc; } librepo-1.12.1/examples/c/fastestmirror_with_callback.c000066400000000000000000000050411372027637700232250ustar00rootroot00000000000000#include #include #include #include static void log_handler_cb(const gchar *log_domain G_GNUC_UNUSED, GLogLevelFlags log_level G_GNUC_UNUSED, const gchar *message, gpointer user_data G_GNUC_UNUSED) { g_print ("%s\n", message); } static void cb(G_GNUC_UNUSED void *clientp, LrFastestMirrorStages stage, void *ptr) { switch (stage) { case LR_FMSTAGE_INIT: printf("Fastest mirror detection started..\n"); break; case LR_FMSTAGE_CACHELOADING: printf("Fastest mirror cache %s used\n", (char *) ptr); break; case LR_FMSTAGE_CACHELOADINGSTATUS: if (ptr) printf("Error while cache loading: %s\n", (char *) ptr); else printf("Cache loaded successfully\n"); break; case LR_FMSTAGE_DETECTION: printf("Fastest mirror detection in progress.. " "(%ld mirror(s) will be pinged)\n", *((long *) ptr)); break; case LR_FMSTAGE_FINISHING: printf("Finishing..\n"); break; case LR_FMSTAGE_STATUS: if (ptr) printf("Fastest mirror detection error: %s\n", (char *) ptr); else printf("Fastest mirror detection successful\n"); break; } } int main(int argc, char *argv[]) { int rc = EXIT_SUCCESS; GSList *list = NULL; GError *tmp_err = NULL; /* g_log_set_handler("librepo", G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_WARNING, log_handler_cb, NULL); */ if (argc < 2) { g_printerr("Usage: %s ...\n", argv[0]); return EXIT_FAILURE; } LrHandle *h = lr_handle_init(); lr_handle_setopt(h, NULL, LRO_FASTESTMIRRORCB, cb); lr_handle_setopt(h, NULL, LRO_FASTESTMIRRORDATA, 1); lr_handle_setopt(h, NULL, LRO_FASTESTMIRRORCACHE, "fastestmirror.cache"); for (int x = 1; x < argc; x++) list = g_slist_prepend(list, argv[x]); list = g_slist_reverse(list); gboolean ret = lr_fastestmirror(h, &list, &tmp_err); lr_handle_free(h); if (!ret) { g_printerr("Error encountered: %s\n", tmp_err->message); g_error_free(tmp_err); rc = EXIT_FAILURE; } for (GSList *elem = list; elem; elem = g_slist_next(elem)) { gchar *url = elem->data; g_print("%s\n", url); } g_slist_free(list); return rc; } librepo-1.12.1/examples/python/000077500000000000000000000000001372027637700164055ustar00rootroot00000000000000librepo-1.12.1/examples/python/download_file.py000077500000000000000000000016051372027637700215720ustar00rootroot00000000000000#!/usr/bin/env python """ librepo - download a file """ import os import sys import shutil import librepo URL = "http://mirrors.fedoraproject.org/mirrorlist?repo=updates-released-f20&arch=x86_64" DEST_FN_1 = "downloaded_mirrorlist_1.txt" DEST_FN_2 = "downloaded_mirrorlist_2.txt" def download_1(): """Example of the simplest usage""" fd = os.open(DEST_FN_1, os.O_RDWR|os.O_CREAT|os.O_TRUNC, 0666) librepo.download_url(URL, fd) def download_2(): """Handle could be used if you need specify some network options like proxy server etc.""" # NOTE: LRO_URLS and LRO_MIRRORLIST options of the handle are ignored!! h = librepo.Handle() #h.setopt(librepo.LRO_PROXY, "http://foo.proxy.bar:8080") fd = os.open(DEST_FN_2, os.O_RDWR|os.O_CREAT|os.O_TRUNC, 0666) librepo.download_url(URL, fd, handle=h) if __name__ == "__main__": download_1() download_2() librepo-1.12.1/examples/python/download_package.py000077500000000000000000000025471372027637700222540ustar00rootroot00000000000000#!/usr/bin/env python """ librepo - download a package """ import os import sys import shutil from pprint import pprint import librepo DESTDIR = "downloaded_metadata" PROGRESSBAR_LEN = 40 finished = False def callback(data, total_to_download, downloaded): """Progress callback""" global finished if total_to_download != downloaded: finished = False if total_to_download <= 0 or finished == True: return completed = int(downloaded / (total_to_download / PROGRESSBAR_LEN)) print "%30s: [%s%s] %8s/%8s\r" % (data, '#'*completed, '-'*(PROGRESSBAR_LEN-completed), int(downloaded), int(total_to_download)), sys.stdout.flush() if total_to_download == downloaded and not finished: print finished = True return if __name__ == "__main__": pkgs = [ ("ImageMagick-djvu", "Packages/i/ImageMagick-djvu-6.7.5.6-3.fc17.i686.rpm"), ("i2c-tools-eepromer", "Packages/i/i2c-tools-eepromer-3.1.0-1.fc17.i686.rpm") ] h = librepo.Handle() h.setopt(librepo.LRO_URLS, ["http://ftp.linux.ncsu.edu/pub/fedora/linux/releases/17/Everything/i386/os/"]) h.setopt(librepo.LRO_REPOTYPE, librepo.LR_YUMREPO) h.setopt(librepo.LRO_PROGRESSCB, callback) h.setopt(librepo.LRO_PROGRESSDATA, "") for pkg_name, pkg_url in pkgs: h.progressdata = pkg_name h.download(pkg_url) librepo-1.12.1/examples/python/download_packageheader.py000077500000000000000000000025101372027637700234130ustar00rootroot00000000000000#!/usr/bin/env python """ librepo - download packages """ import librepo if __name__ == "__main__": # Setup logging def debug_function(msg, _): print msg librepo.set_debug_log_handler(debug_function) # Prepare handle h = librepo.Handle() h.urls = ["http://mirror.karneval.cz/pub/linux/fedora/linux/releases/20/Fedora/x86_64/os/"] h.repotype = librepo.YUMREPO # Prepare list of targets packages = [] target = librepo.PackageTarget("Packages/a/abrt-2.1.9-1.fc20.x86_64.rpm", handle=h, dest="abrt.header", byterangestart=1384, byterangeend=100204) packages.append(target) librepo.download_packages(packages) for target in packages: print "### %s: %s" % (target.local_path, target.err or "OK") print "Relative URL: ", target.relative_url print "Destination: ", target.dest print "Base URL: ", target.base_url print "Checksum type: ", target.checksum_type print "Expected checksum: ", target.checksum print "Resume: ", bool(target.resume) print "Local path: ", target.local_path print "Error: ", target.err print librepo-1.12.1/examples/python/download_packages.py000077500000000000000000000032201372027637700224240ustar00rootroot00000000000000#!/usr/bin/env python """ librepo - download packages """ import librepo if __name__ == "__main__": # Setup logging def debug_function(msg, _): print msg librepo.set_debug_log_handler(debug_function) # Prepare handle h = librepo.Handle() h.urls = ["http://beaker-project.org/yum/client-testing/Fedora21/"] h.repotype = librepo.YUMREPO # Prepare list of targets packages = [] target = librepo.PackageTarget("beaker-19.1-1.fc19.src.rpm", handle=h, checksum_type=librepo.SHA256, checksum="3a07327702d15de2707518eb777c0837b2fc7c0cf4cb757331982f5ce6053867", resume=True) packages.append(target) target = librepo.PackageTarget("beaker-client-19.1-1.fc19.noarch.rpm", handle=h, expectedsize=381148) packages.append(target) target = librepo.PackageTarget("rhts-4.65-1.fc19.src.rpm", handle=h) packages.append(target) librepo.download_packages(packages) for target in packages: print "### %s: %s" % (target.local_path, target.err or "OK") print "Relative URL: ", target.relative_url print "Destination: ", target.dest print "Base URL: ", target.base_url print "Checksum type: ", target.checksum_type print "Expected checksum: ", target.checksum print "Resume: ", bool(target.resume) print "Local path: ", target.local_path print "Error: ", target.err print librepo-1.12.1/examples/python/download_packages_with_cbs.py000077500000000000000000000074701372027637700243210ustar00rootroot00000000000000#!/usr/bin/env python """ librepo - Example of download_packages() function with use of end, failure and mirrorfailure callbacks. """ import librepo if __name__ == "__main__": # Setup logging #def debug_function(msg, _): # print "DEBUG: %s" % msg #librepo.set_debug_log_handler(debug_function) # Prepare handle h = librepo.Handle() h.urls = ["http://beaker-project.org/yum/client-testing/Fedora19/"] h.repotype = librepo.YUMREPO # Callbacks def endcb(data, status, msg): print "EndCb: Download of %s finished with status:" % data, if status == librepo.TRANSFER_SUCCESSFUL: print "Successfully" elif status == librepo.TRANSFER_ALREADYEXISTS: print "Already exists" else: print "Error: %s" % msg def mirrorfailurecb(data, msg, url): print "MirrorFailureCb: Download of %s from %s failed with: %s" % \ (data, url, msg) # Prepare list of targets packages = [] target = librepo.PackageTarget("beaker-0.14.0-1.fc18.src.rpm", handle=h, checksum_type=librepo.SHA256, checksum="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", resume=True, cbdata="beaker-0.14.0-1.fc18.src.rpm", endcb=endcb, mirrorfailurecb=mirrorfailurecb) packages.append(target) target = librepo.PackageTarget("beaker-0.13.2-1.fc17.noarch.rpm", handle=h, checksum_type=librepo.SHA256, checksum="foobar", cbdata="beaker-0.13.2-1.fc17.noarch.rpm (bad checksum)", endcb=endcb, mirrorfailurecb=mirrorfailurecb) packages.append(target) target = librepo.PackageTarget("beaker-0.13.2-1.fc17.src.rpm", handle=h, checksum_type=librepo.SHA256, checksum="xyz", cbdata="beaker-0.13.2-1.fc17.src.rpm (bad checksum)", endcb=endcb, mirrorfailurecb=mirrorfailurecb) packages.append(target) target = librepo.PackageTarget("beaker-client-0.14.1-1.fc18.noarch.rpm", handle=h, expectedsize=333333333333333, cbdata="beaker-client-0.14.1-1.fc18.noarch.rpm (bad size)", endcb=endcb, mirrorfailurecb=mirrorfailurecb) packages.append(target) target = librepo.PackageTarget("rhts-4.56-1.fc17.src.rpm_bad_filename", handle=h, cbdata="rhts-4.56-1.fc17.src.rpm_bad_filename (bad path)", endcb=endcb, mirrorfailurecb=mirrorfailurecb) packages.append(target) librepo.download_packages(packages, failfast=False) for target in packages: print "### %s: %s" % (target.local_path, target.err or "OK") print "Relative URL: ", target.relative_url print "Destination: ", target.dest print "Base URL: ", target.base_url print "Checksum type: ", target.checksum_type print "Expected checksum: ", target.checksum print "Resume: ", bool(target.resume) print "Local path: ", target.local_path print "Error: ", target.err print librepo-1.12.1/examples/python/download_packages_with_fastestmirror.py000077500000000000000000000031461372027637700264520ustar00rootroot00000000000000#!/usr/bin/env python """ librepo - download packages """ import os import os.path import time import librepo CACHE = "fastestmirror.cache" LIBREPOPKG = "librepo-1.2.1-2.fc20.x86_64.rpm" LAMEPKG = "lame-3.99.5-2.fc19.x86_64.rpm" if __name__ == "__main__": # Setup logging def debug_function(msg, _): print msg #librepo.set_debug_log_handler(debug_function) # Remove packages if already exists def remove_pkg(filename): if os.path.exists(filename): os.remove(filename) remove_pkg(LIBREPOPKG) remove_pkg(LAMEPKG) # Prepare list of targets packages = [] # Prepare first target h1 = librepo.Handle() h1.metalinkurl = "https://mirrors.fedoraproject.org/metalink?repo=fedora-20&arch=x86_64" h1.repotype = librepo.YUMREPO h1.fastestmirror = True h1.fastestmirrorcache = CACHE target = librepo.PackageTarget("Packages/l/"+LIBREPOPKG, handle=h1) packages.append(target) # Prepare second target h2 = librepo.Handle() h2.mirrorlisturl = "http://mirrors.rpmfusion.org/mirrorlist?repo=free-fedora-19&arch=x86_64" h2.repotype = librepo.YUMREPO h2.fastestmirror = True h2.fastestmirrorcache = CACHE target = librepo.PackageTarget(LAMEPKG, handle=h2) packages.append(target) t = time.time() librepo.download_packages(packages) print "Download duration: {0}s\n".format((time.time() - t)) for target in packages: print "### %s: %s" % (target.local_path, target.err or "OK") print "Local path: ", target.local_path print "Error: ", target.err print librepo-1.12.1/examples/python/download_packages_with_fastestmirror_with_cache_and_cbs.py000077500000000000000000000064571372027637700323110ustar00rootroot00000000000000#!/usr/bin/env python """ librepo - download packages from repo with fastestmirror and cache enabled """ import os import os.path import time import librepo METALINKURL = "https://mirrors.fedoraproject.org/metalink?repo=fedora-19&arch=x86_64" PACKAGEPATH = "Packages/l/" PACKAGE = "librepo-0.0.4-1.fc19.i686.rpm" CACHE = "fastestmirror.cache" def fastestmirrorstatuscallback(userdata, stage, data): if stage == librepo.FMSTAGE_INIT: print "Fastest mirror detection started.." elif stage == librepo.FMSTAGE_CACHELOADING: print "Fastest mirror cache used: {0}".format(data) elif stage == librepo.FMSTAGE_CACHELOADINGSTATUS: if data: print "Error while cache loading: {0}".format(data) else: print "Cache loaded successfully" elif stage == librepo.FMSTAGE_DETECTION: print "Fastest mirror detection in progress.. (%s mirror(s) will be pinged)" % data elif stage == librepo.FMSTAGE_FINISHING: print "Fastest mirror detection finishing.." elif stage == librepo.FMSTAGE_STATUS: if data: print "Fastest mirror detection error: {0}".format(data) else: print "Fastest mirror detection successful" else: print "UNKNOWN STAGE of Fastest mirror detection" if __name__ == "__main__": # Setup logging def debug_function(msg, _): print msg #librepo.set_debug_log_handler(debug_function) def remove_cache(): if os.path.exists(CACHE): print "Removing %s" % CACHE os.remove(CACHE) def remove_package(): if os.path.exists(PACKAGE): print "Removing %s" % PACKAGE os.remove(PACKAGE) remove_cache() remove_package() print "\n1st run: Basic download" h = librepo.Handle() h.interruptible = True h.metalinkurl = METALINKURL h.repotype = librepo.YUMREPO packages = [librepo.PackageTarget(PACKAGEPATH+PACKAGE, handle=h)] t = time.time() librepo.download_packages(packages) t1 = time.time() - t print "### %s: %s (Time: %s)" % ( packages[0].local_path, packages[0].err or "OK", t1) remove_package() print "\n2nd run: Download with fastestmirror - cache will be builded" h = librepo.Handle() h.interruptible = True h.metalinkurl = METALINKURL h.repotype = librepo.YUMREPO h.fastestmirror = True h.fastestmirrorcache = CACHE h.fastestmirrorcb = fastestmirrorstatuscallback packages = [librepo.PackageTarget(PACKAGEPATH+PACKAGE, handle=h)] t = time.time() librepo.download_packages(packages) t2 = time.time() - t print "### %s: %s (Time: %s)" % ( packages[0].local_path, packages[0].err or "OK", t2) remove_package() print "\n3th run: Download with fastestmirror - cache should be used" h = librepo.Handle() h.interruptible = True h.metalinkurl = METALINKURL h.repotype = librepo.YUMREPO h.fastestmirror = True h.fastestmirrorcache = CACHE h.fastestmirrorcb = fastestmirrorstatuscallback packages = [librepo.PackageTarget(PACKAGEPATH+PACKAGE, handle=h)] t = time.time() librepo.download_packages(packages) t3 = time.time() - t print "### %s: %s (Time: %s)" % ( packages[0].local_path, packages[0].err or "OK", t3) remove_package() remove_cache() librepo-1.12.1/examples/python/download_remote_yum_repo.py000077500000000000000000000056641372027637700240760ustar00rootroot00000000000000#!/usr/bin/python """ librepo - example of usage """ import os import sys import shutil from pprint import pprint import librepo DESTDIR = "downloaded_metadata" PROGRESSBAR_LEN = 50 def callback(data, total_to_download, downloaded): """Progress callback""" if total_to_download <= 0: return completed = int(downloaded / (total_to_download / PROGRESSBAR_LEN)) print "[%s%s] %8s/%8s (%s)\r" % ('#'*completed, '-'*(PROGRESSBAR_LEN-completed), int(downloaded), int(total_to_download), data), sys.stdout.flush() if __name__ == "__main__": # Prepare destination directory if os.path.exists(DESTDIR): if not os.path.isdir(DESTDIR): raise IOError("%s is not a directory" % DESTDIR) shutil.rmtree(DESTDIR) os.mkdir(DESTDIR) h = librepo.Handle() # Handle represents a download configuration r = librepo.Result() # Result represents an existing/downloaded repository # --- Mandatory arguments ------------------------------------------- # URL of repository or URL of metalink/mirrorlist h.setopt(librepo.LRO_URLS, ["http://ftp.linux.ncsu.edu/pub/fedora/linux/releases/19/Everything/i386/os/"]) #h.setopt(librepo.LRO_MIRRORLIST, "https://mirrors.fedoraproject.org/metalink?repo=fedora-source-17&arch=i386") # Note: LRO_URLS and LRO_MIRRORLIST could be set and used simultaneously # and if download from LRO_URLS failed, then mirrorlist is used # Type of repository h.setopt(librepo.LRO_REPOTYPE, librepo.LR_YUMREPO) # --- Optional arguments -------------------------------------------- # Make download interruptible h.setopt(librepo.LRO_INTERRUPTIBLE, True) # Destination directory for metadata h.setopt(librepo.LRO_DESTDIR, DESTDIR) # Check checksum of all files (if checksum is available in repomd.xml) h.setopt(librepo.LRO_CHECKSUM, True) # Callback to display progress of downloading h.setopt(librepo.LRO_PROGRESSCB, callback) # Set user data for the callback h.setopt(librepo.LRO_PROGRESSDATA, {'test': 'dict', 'foo': 'bar'}) # Download only filelists.xml, prestodelta.xml # Note: repomd.xml is downloaded implicitly! # Note: If LRO_YUMDLIST is None -> all files are downloaded h.setopt(librepo.LRO_YUMDLIST, ["filelists", "prestodelta"]) h.perform(r) # Get and show results pprint (r.getinfo(librepo.LRR_YUM_REPO)) pprint (r.getinfo(librepo.LRR_YUM_REPOMD)) # Whoops... I forget to download primary.xml.. Lets fix it! # Set LRO_UPDATE - only update existing Result h.setopt(librepo.LRO_UPDATE, True) h.setopt(librepo.LRO_YUMDLIST, ["primary"]) h.perform(r) # List of mirrors # (In this case no mirrorlist is used -> list will contain only one url) # Example of access info via attr insted of .getinfo() method pprint (h.mirrors) # Get and show final results pprint (r.getinfo(librepo.LRR_YUM_REPO)) pprint (r.getinfo(librepo.LRR_YUM_REPOMD)) librepo-1.12.1/examples/python/read_metalink_of_downloaded_repo.py000077500000000000000000000020321372027637700254670ustar00rootroot00000000000000#!/usr/bin/python """ librepo - example of usage """ import os import sys import librepo import pprint DESTDIR = "downloaded_metadata" if __name__ == "__main__": h = librepo.Handle() r = librepo.Result() # Correct repotype is important. Without repotype # metalink parser doesn't know suffix which should # be stripped off from the mirrors urls. h.setopt(librepo.LRO_REPOTYPE, librepo.LR_YUMREPO) # Set local mirrorlist file as mirrorlist if os.path.isfile(os.path.join(DESTDIR, "mirrorlist")): h.mirrorlist = os.path.join(DESTDIR, "mirrorlist") elif os.path.isfile(os.path.join(DESTDIR, "metalink.xml")): h.mirrorlist = os.path.join(DESTDIR, "metalink.xml") else: print "No mirrorlist of downloaded repodata available" sys.exit(0) # Download only the mirrorlist during perform() call. h.setopt(librepo.LRO_FETCHMIRRORS, True) h.perform(r) print "Urls in mirrorlist:" print h.mirrors print "Metalink file content:" pprint.pprint(h.metalink) librepo-1.12.1/examples/python/using_client_ssl_certificates.py000066400000000000000000000015501372027637700250510ustar00rootroot00000000000000#!/usr/bin/env python """ librepo - download a package using client SSL certificate """ import librepo if __name__ == "__main__": h = librepo.Handle() h.setopt(librepo.LRO_REPOTYPE, librepo.LR_YUMREPO) h.setopt(librepo.LRO_URLS, ["https://my-ssl-repo/fedora/20/x86_64/os/"]) # The client certificate .pem file may include just the public certificate # or may include an entire certificate chain including # public key, private key, and root certificates. h.setopt(librepo.LRO_SSLCLIENTCERT, "/etc/pki/client.pem") # In case the .pem just contains the public key, set the private key h.setopt(librepo.LRO_SSLCLIENTKEY, "/etc/client.key") # Set the certificate authority h.setopt(librepo.LRO_SSLCACERT, "/etc/ca.pem") h.progressdata = "i2c-tools-eepromer" h.download("packages/i2c-tools-eepromer-24-1.x86_64.rpm") librepo-1.12.1/examples/python/verify_local_yum_metadata.py000077500000000000000000000022221372027637700241700ustar00rootroot00000000000000#!/usr/bin/python """ Example: Verify checksum of local metadata Use case: We have some incomplete metadata localy. They are incomplete because they doesn't contain all files specified in repomd.xml. They contains only primary.xml and filelists.xml. We want to check checksum of this metadata. """ import sys import librepo METADATA_PATH = "downloaded_metadata" if __name__ == "__main__": h = librepo.Handle() r = librepo.Result() # Repository with repodata in the rpm-md format h.setopt(librepo.LRO_REPOTYPE, librepo.LR_YUMREPO) # Path to the metadata h.setopt(librepo.LRO_URLS, [METADATA_PATH]) # Do not duplicate (copy) the metadata h.setopt(librepo.LRO_LOCAL, True) # Check checksum of metadata h.setopt(librepo.LRO_CHECKSUM, True) # Ignore missing metadata files h.setopt(librepo.LRO_IGNOREMISSING, True) try: h.perform(r) except librepo.LibrepoException as e: rc, msg, general_msg = e if rc == librepo.LRE_BADCHECKSUM: print "Corrupted metadata! (%s)" % msg else: print "Other error: %s" % msg sys.exit(1) print "Metadata are fine!" librepo-1.12.1/examples/python/yum_repo_localisation.py000077500000000000000000000020551372027637700233640ustar00rootroot00000000000000#!/usr/bin/python """ Example: Localize metadata files of a local repository Use case: We have a local repositository in the rpm-md format and want to paths to all its metadata files. Repomd content is just a bonus. """ import sys import librepo import pprint METADATA_PATH = "downloaded_metadata" if __name__ == "__main__": h = librepo.Handle() r = librepo.Result() # Repository with repodata in the rpm-md format h.setopt(librepo.LRO_REPOTYPE, librepo.LR_YUMREPO) # Path to metadata h.setopt(librepo.LRO_URLS, [METADATA_PATH]) # Do not duplicate (copy) metadata, just localise them h.setopt(librepo.LRO_LOCAL, True) try: h.perform(r) except librepo.LibrepoException as e: rc, msg, general_msg = e print "Error: %s" % msg sys.exit(1) print "Repomd content:" pprint.pprint(r.getinfo(librepo.LRR_YUM_REPOMD)) print "\nPaths to metadata files:" for data_type, path in r.getinfo(librepo.LRR_YUM_REPO).iteritems(): print "%15s: %s" % (data_type, path) sys.exit(0) librepo-1.12.1/examples/python/yum_repo_simple_download.py000077500000000000000000000021421372027637700240600ustar00rootroot00000000000000#!/usr/bin/python """ Example: Simple download whole repository This example uses more "pythonic" way of usage. Instead of use setopt() method it uses class properties. Use case: We have a metalink url of a repository and we want do download complete repository metadata. """ import librepo # Metalink URL METALINK_URL = "https://mirrors.fedoraproject.org/metalink?repo=fedora-19&arch=x86_64" # Destination directory (note: This directory must exists!) DESTDIR = "downloaded_metadata" if __name__ == "__main__": h = librepo.Handle() r = librepo.Result() # Repository with repodata in the rpm-md format h.repotype = librepo.LR_YUMREPO # Set metalink url h.mirrorlist = METALINK_URL # Destination directory for metadata h.destdir = DESTDIR # Use the fastest mirror h.fastestmirror = True try: h.perform(r) except librepo.LibrepoException as e: # rc - Return code (integer value) # msg - Detailed error message (string) # general_msg - Error message based on rc (string) rc, msg, general_msg = e print "Error: %s" % msg librepo-1.12.1/librepo.spec000066400000000000000000000103051372027637700155550ustar00rootroot00000000000000%global libcurl_version 7.28.0 %define __cmake_in_source_build 1 %if 0%{?rhel} && 0%{?rhel} <= 7 # Do not build bindings for python3 for RHEL <= 7 %bcond_with python3 # python-flask is not in RHEL7 %bcond_with pythontests %else %bcond_without python3 %bcond_without pythontests %endif %if 0%{?rhel} > 7 || 0%{?fedora} > 29 # Do not build bindings for python2 for RHEL > 7 and Fedora > 29 %bcond_with python2 %else %bcond_without python2 %endif %if 0%{?rhel} %bcond_with zchunk %else %bcond_without zchunk %endif %global dnf_conflict 2.8.8 Name: librepo Version: 1.12.1 Release: 1%{?dist} Summary: Repodata downloading library License: LGPLv2+ URL: https://github.com/rpm-software-management/librepo Source0: %{url}/archive/%{version}/%{name}-%{version}.tar.gz BuildRequires: cmake BuildRequires: gcc BuildRequires: check-devel BuildRequires: doxygen BuildRequires: pkgconfig(glib-2.0) BuildRequires: gpgme-devel BuildRequires: libattr-devel BuildRequires: libcurl-devel >= %{libcurl_version} BuildRequires: pkgconfig(libxml-2.0) BuildRequires: pkgconfig(libcrypto) BuildRequires: pkgconfig(openssl) %if %{with zchunk} BuildRequires: pkgconfig(zck) >= 0.9.11 %endif Requires: libcurl%{?_isa} >= %{libcurl_version} %description A library providing C and Python (libcURL like) API to downloading repository metadata. %package devel Summary: Repodata downloading library Requires: %{name}%{?_isa} = %{version}-%{release} %description devel Development files for librepo. %if %{with python2} %package -n python2-%{name} Summary: Python bindings for the librepo library %{?python_provide:%python_provide python2-%{name}} %if 0%{?rhel} && 0%{?rhel} <= 7 BuildRequires: python-sphinx %else BuildRequires: python2-sphinx %endif BuildRequires: python2-devel %if %{with pythontests} BuildRequires: python2-flask BuildRequires: python2-nose BuildRequires: python2-requests %if (0%{?rhel} && 0%{?rhel} <= 7) BuildRequires: pyxattr BuildRequires: pygpgme %else BuildRequires: python2-pyxattr BuildRequires: python2-gpg %endif %endif # endif with pythontests Requires: %{name}%{?_isa} = %{version}-%{release} Conflicts: python2-dnf < %{dnf_conflict} %description -n python2-%{name} Python 2 bindings for the librepo library. %endif %if %{with python3} %package -n python3-%{name} Summary: Python 3 bindings for the librepo library %{?python_provide:%python_provide python3-%{name}} BuildRequires: python3-devel %if %{with pythontests} BuildRequires: python3-gpg BuildRequires: python3-flask BuildRequires: python3-nose BuildRequires: python3-pyxattr BuildRequires: python3-requests %endif BuildRequires: python3-sphinx Requires: %{name}%{?_isa} = %{version}-%{release} # Obsoletes Fedora 27 package Obsoletes: platform-python-%{name} < %{version}-%{release} Conflicts: python3-dnf < %{dnf_conflict} %description -n python3-%{name} Python 3 bindings for the librepo library. %endif %prep %autosetup -p1 mkdir build-py2 mkdir build-py3 %build %if %{with python2} pushd build-py2 %cmake -DPYTHON_DESIRED:FILEPATH=%{__python2} %{!?with_zchunk:-DWITH_ZCHUNK=OFF} -DENABLE_PYTHON_TESTS=%{?with_pythontests:ON}%{!?with_pythontests:OFF} .. %make_build popd %endif %if %{with python3} pushd build-py3 %cmake -DPYTHON_DESIRED:FILEPATH=%{__python3} %{!?with_zchunk:-DWITH_ZCHUNK=OFF} -DENABLE_PYTHON_TESTS=%{?with_pythontests:ON}%{!?with_pythontests:OFF} .. %make_build popd %endif %check %if %{with python2} pushd build-py2 #ctest -VV make ARGS="-V" test popd %endif %if %{with python3} pushd build-py3 #ctest -VV make ARGS="-V" test popd %endif %install %if %{with python2} pushd build-py2 %make_install popd %endif %if %{with python3} pushd build-py3 %make_install popd %endif %if 0%{?rhel} && 0%{?rhel} <= 7 %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %else %ldconfig_scriptlets %endif %files %license COPYING %doc README.md %{_libdir}/%{name}.so.* %files devel %{_libdir}/%{name}.so %{_libdir}/pkgconfig/%{name}.pc %{_includedir}/%{name}/ %if %{with python2} %files -n python2-%{name} %{python2_sitearch}/%{name}/ %endif %if %{with python3} %files -n python3-%{name} %{python3_sitearch}/%{name}/ %endif %changelog librepo-1.12.1/librepo/000077500000000000000000000000001372027637700147025ustar00rootroot00000000000000librepo-1.12.1/librepo/CMakeLists.txt000066400000000000000000000035411372027637700174450ustar00rootroot00000000000000SET (librepo_SRCS checksum.c downloader.c downloadtarget.c fastestmirror.c gpg.c handle.c lrmirrorlist.c metalink.c metadata_downloader.c mirrorlist.c package_downloader.c rcodes.c repoconf.c repomd.c repoutil_yum.c result.c url_substitution.c util.c xmlparser.c yum.c) SET(librepo_HEADERS checksum.h fastestmirror.h gpg.h handle.h librepo.h metadata_downloader.h metalink.h mirrorlist.h package_downloader.h rcodes.h repoconf.h repomd.h repoutil_yum.h result.h types.h url_substitution.h util.h version.h xmlparser.h yum.h downloader.h downloader_internal.h downloadtarget.h) ADD_LIBRARY(librepo SHARED ${librepo_SRCS}) TARGET_LINK_LIBRARIES(librepo ${LIBXML2_LIBRARIES} ${CURL_LIBRARY} ${LIBCRYPTO_LIBRARIES} ${GPGME_VANILLA_LIBRARIES} ${GLIB2_LIBRARIES} ) IF (WITH_ZCHUNK) TARGET_LINK_LIBRARIES(librepo ${ZCHUNKLIB_LIBRARIES}) ENDIF (WITH_ZCHUNK) SET_TARGET_PROPERTIES(librepo PROPERTIES OUTPUT_NAME "repo") SET_TARGET_PROPERTIES(librepo PROPERTIES SOVERSION 0) #SET_TARGET_PROPERTIES(librepo PROPERTIES VERSION "0") CONFIGURE_FILE("librepo.pc.cmake" "${CMAKE_BINARY_DIR}/librepo.pc" @ONLY) CONFIGURE_FILE("version.h.in" "${CMAKE_CURRENT_SOURCE_DIR}/version.h" @ONLY) IF (CMAKE_SIZEOF_VOID_P MATCHES "8") SET (LIB_SUFFIX "64") ENDIF (CMAKE_SIZEOF_VOID_P MATCHES "8") INSTALL(FILES ${librepo_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/librepo) INSTALL(TARGETS librepo LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) INSTALL(FILES "${CMAKE_BINARY_DIR}/librepo.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") ADD_SUBDIRECTORY(python) librepo-1.12.1/librepo/checksum.c000066400000000000000000000200471372027637700166530ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include "cleanup.h" #include "checksum.h" #include "rcodes.h" #include "util.h" #define BUFFER_SIZE 2048 #define MAX_CHECKSUM_NAME_LEN 7 LrChecksumType lr_checksum_type(const char *type) { size_t len; char type_lower[MAX_CHECKSUM_NAME_LEN+1]; if (!type) return LR_CHECKSUM_UNKNOWN; len = strlen(type); if (len > MAX_CHECKSUM_NAME_LEN) return LR_CHECKSUM_UNKNOWN; for (size_t x = 0; x <= len; x++) type_lower[x] = tolower(type[x]); if (!strncmp(type_lower, "md", 2)) { // MD* family char *md_type = type_lower + 2; if (!strcmp(md_type, "5")) return LR_CHECKSUM_MD5; } else if (!strncmp(type_lower, "sha", 3)) { // SHA* family char *sha_type = type_lower + 3; if (!strcmp(sha_type, "")) return LR_CHECKSUM_SHA1; else if (!strcmp(sha_type, "1")) return LR_CHECKSUM_SHA1; else if (!strcmp(sha_type, "224")) return LR_CHECKSUM_SHA224; else if (!strcmp(sha_type, "256")) return LR_CHECKSUM_SHA256; else if (!strcmp(sha_type, "384")) return LR_CHECKSUM_SHA384; else if (!strcmp(sha_type, "512")) return LR_CHECKSUM_SHA512; } return LR_CHECKSUM_UNKNOWN; } const char * lr_checksum_type_to_str(LrChecksumType type) { switch (type) { case LR_CHECKSUM_UNKNOWN: return "Unknown checksum"; case LR_CHECKSUM_MD5: return "md5"; case LR_CHECKSUM_SHA1: return "sha1"; case LR_CHECKSUM_SHA224: return "sha224"; case LR_CHECKSUM_SHA256: return "sha256"; case LR_CHECKSUM_SHA384: return "sha384"; case LR_CHECKSUM_SHA512: return "sha512"; } return NULL; } char * lr_checksum_fd(LrChecksumType type, int fd, GError **err) { unsigned int len; ssize_t readed; char buf[BUFFER_SIZE]; unsigned char raw_checksum[EVP_MAX_MD_SIZE]; char *checksum; EVP_MD_CTX *ctx; const EVP_MD *ctx_type; assert(fd > -1); assert(!err || *err == NULL); switch (type) { case LR_CHECKSUM_MD5: ctx_type = EVP_md5(); break; case LR_CHECKSUM_SHA1: ctx_type = EVP_sha1(); break; case LR_CHECKSUM_SHA224: ctx_type = EVP_sha224(); break; case LR_CHECKSUM_SHA256: ctx_type = EVP_sha256(); break; case LR_CHECKSUM_SHA384: ctx_type = EVP_sha384(); break; case LR_CHECKSUM_SHA512: ctx_type = EVP_sha512(); break; case LR_CHECKSUM_UNKNOWN: default: g_debug("%s: Unknown checksum type", __func__); assert(0); g_set_error(err, LR_CHECKSUM_ERROR, LRE_BADFUNCARG, "Unknown checksum type: %d", type); return NULL; } ctx = EVP_MD_CTX_create(); if (!ctx) { g_set_error(err, LR_CHECKSUM_ERROR, LRE_OPENSSL, "EVP_MD_CTX_create() failed"); return NULL; } if (!EVP_DigestInit_ex(ctx, ctx_type, NULL)) { g_set_error(err, LR_CHECKSUM_ERROR, LRE_OPENSSL, "EVP_DigestInit_ex() failed"); EVP_MD_CTX_destroy(ctx); return NULL; } if (lseek(fd, 0, SEEK_SET) == -1) { g_set_error(err, LR_CHECKSUM_ERROR, LRE_IO, "Cannot seek to the begin of the file. " "lseek(%d, 0, SEEK_SET) error: %s", fd, g_strerror(errno)); return NULL; } while ((readed = read(fd, buf, BUFFER_SIZE)) > 0) if (!EVP_DigestUpdate(ctx, buf, readed)) { g_set_error(err, LR_CHECKSUM_ERROR, LRE_OPENSSL, "EVP_DigestUpdate() failed"); return NULL; } if (readed == -1) { EVP_MD_CTX_destroy(ctx); g_set_error(err, LR_CHECKSUM_ERROR, LRE_IO, "read(%d) failed: %s", fd, g_strerror(errno)); return NULL; } if (!EVP_DigestFinal_ex(ctx, raw_checksum, &len)) { g_set_error(err, LR_CHECKSUM_ERROR, LRE_OPENSSL, "EVP_DigestFinal_ex() failed"); return NULL; } EVP_MD_CTX_destroy(ctx); checksum = lr_malloc0(sizeof(char) * (len * 2 + 1)); for (size_t x = 0; x < len; x++) sprintf(checksum+(x*2), "%02x", raw_checksum[x]); return checksum; } gboolean lr_checksum_fd_cmp(LrChecksumType type, int fd, const char *expected, gboolean caching, gboolean *matches, GError **err) { return lr_checksum_fd_compare(type, fd, expected, caching, matches, NULL, err); } gboolean lr_checksum_fd_compare(LrChecksumType type, int fd, const char *expected, gboolean caching, gboolean *matches, gchar **calculated, GError **err) { _cleanup_free_ gchar *checksum = NULL; assert(fd >= 0); assert(!err || *err == NULL); *matches = FALSE; if (!expected) { g_set_error(err, LR_CHECKSUM_ERROR, LRE_BADFUNCARG, "No expected checksum passed"); return FALSE; } if (caching) { // Load cached checksum if enabled and used struct stat st; if (fstat(fd, &st) == 0) { _cleanup_free_ gchar *key = NULL; char buf[256]; key = g_strdup_printf("user.Zif.MdChecksum[%llu]", (unsigned long long) st.st_mtime); #if __APPLE__ ssize_t attr_size = fgetxattr(fd, key, &buf, sizeof(buf), 0, 0); #else ssize_t attr_size = fgetxattr(fd, key, &buf, sizeof(buf)); #endif if (attr_size != -1) { // Cached checksum found g_debug("%s: Using checksum cached in xattr: [%s] %s", __func__, key, buf); size_t expected_len = strlen(expected); // xattr may contain null terminator (+1 byte) *matches = (attr_size == expected_len || attr_size == expected_len + 1) && memcmp(expected, buf, attr_size) == 0; return TRUE; } } } checksum = lr_checksum_fd(type, fd, err); if (!checksum) return FALSE; *matches = (strcmp(expected, checksum)) ? FALSE : TRUE; if (caching && *matches) { // Store checksum as extended file attribute if caching is enabled struct stat st; if (fstat(fd, &st) == 0) { _cleanup_free_ gchar *key = NULL; key = g_strdup_printf("user.Zif.MdChecksum[%llu]", (unsigned long long) st.st_mtime); #if __APPLE__ fsetxattr(fd, key, checksum, strlen(checksum)+1, 0, 0); #else fsetxattr(fd, key, checksum, strlen(checksum)+1, 0); #endif } } if (calculated) *calculated = g_strdup(checksum); return TRUE; } librepo-1.12.1/librepo/checksum.h000066400000000000000000000101441372027637700166550ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_CHECKSUM_H__ #define __LR_CHECKSUM_H__ #include G_BEGIN_DECLS /** \defgroup checksum Checksum calculation and checking * \addtogroup checksum * @{ */ /** Enum of supported checksum types. * NOTE! This enum guarantee to be sorted by "hash quality" */ typedef enum { LR_CHECKSUM_UNKNOWN, LR_CHECKSUM_MD5, /* The most weakest hash */ LR_CHECKSUM_SHA1, /* | */ LR_CHECKSUM_SHA224, /* | */ LR_CHECKSUM_SHA256, /* | */ LR_CHECKSUM_SHA384, /* \|/ */ LR_CHECKSUM_SHA512, /* The most secure hash */ } LrChecksumType; /** Convert checksum name (string) to ::LrChecksumType. * @param type String with a checksum name (e.g. "sha1", "SHA256", ...) * @return ::LrChecksumType value representing the checksum * or LR_CHECKSUM_UNKNOWN */ LrChecksumType lr_checksum_type(const char *type); /** Convert LrChecksumType to string * @param type Checksum type * @return Constant string with name of the checksum */ const char * lr_checksum_type_to_str(LrChecksumType type); /** Calculate checksum for data pointed by file descriptor. * @param type Checksum type * @param fd Opened file descriptor. Function seeks to the begin * of the file. * @param err GError ** * @return Malloced checksum string or NULL on error. */ char * lr_checksum_fd(LrChecksumType type, int fd, GError **err); /** Calculate checksum for data pointed by file descriptor and * compare it to the expected checksum value. * @param type Checksum type * @param fd File descriptor * @param expected String with expected checksum value * @param caching Cache/Use cached checksum value as extended file attr. * @param matches Set pointed variable to TRUE if checksum matches. * @param err GError ** * @return returns TRUE if error is not set and FALSE if it is */ gboolean lr_checksum_fd_cmp(LrChecksumType type, int fd, const char *expected, gboolean caching, gboolean *matches, GError **err); /** Calculate checksum for data pointed by file descriptor and * compare it to the expected checksum value. * @param type Checksum type * @param fd File descriptor * @param expected String with expected checksum value * @param caching Cache/Use cached checksum value as extended file attr. * @param matches Set pointed variable to TRUE if checksum matches. * @param calculated If not NULL, the calculated checksum will be pointed * here, the pointed string must be freed by caller. * @param err GError ** * @return returns TRUE if error is not set and FALSE if it is */ gboolean lr_checksum_fd_compare(LrChecksumType type, int fd, const char *expected, gboolean caching, gboolean *matches, gchar **calculated, GError **err); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/cleanup.h000066400000000000000000000104161372027637700165040ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * * Copyright (C) 2012 Colin Walters . * Copyright (C) 2014 Richard Hughes * Copyright (C) 2014 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* This file was taken from libhif (https://github.com/hughsie/libhif) */ #ifndef __LR_CLEANUP_H__ #define __LR_CLEANUP_H__ #include #include G_BEGIN_DECLS static inline void lr_close(int fildes) { if (fildes < 0) return; close(fildes); } #define LR_DEFINE_CLEANUP_FUNCTION(Type, name, func) \ static inline void name (Type *v) \ { \ func (*v); \ } #define LR_DEFINE_CLEANUP_FUNCTION0(Type, name, func) \ static inline void name (Type *v) \ { \ if (*v) \ func (*v); \ } #define LR_DEFINE_CLEANUP_FUNCTIONt(Type, name, func) \ static inline void name (Type *v) \ { \ if (*v) \ func (*v, TRUE); \ } LR_DEFINE_CLEANUP_FUNCTION0(GArray*, lr_local_array_unref, g_array_unref) LR_DEFINE_CLEANUP_FUNCTION0(GChecksum*, lr_local_checksum_free, g_checksum_free) LR_DEFINE_CLEANUP_FUNCTION0(GDir*, lr_local_dir_close, g_dir_close) LR_DEFINE_CLEANUP_FUNCTION0(GError*, lr_local_free_error, g_error_free) LR_DEFINE_CLEANUP_FUNCTION0(GHashTable*, lr_local_hashtable_unref, g_hash_table_unref) #if GLIB_CHECK_VERSION(2, 32, 0) LR_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, lr_local_keyfile_unref, g_key_file_unref) #endif LR_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, lr_local_keyfile_free, g_key_file_free) LR_DEFINE_CLEANUP_FUNCTION0(GPtrArray*, lr_local_ptrarray_unref, g_ptr_array_unref) LR_DEFINE_CLEANUP_FUNCTION0(GTimer*, lr_local_destroy_timer, g_timer_destroy) LR_DEFINE_CLEANUP_FUNCTION0(GDateTime*, lr_local_date_time_unref, g_date_time_unref) LR_DEFINE_CLEANUP_FUNCTIONt(GString*, lr_local_free_string, g_string_free) LR_DEFINE_CLEANUP_FUNCTION(char**, lr_local_strfreev, g_strfreev) LR_DEFINE_CLEANUP_FUNCTION(GList*, lr_local_free_list, g_list_free) LR_DEFINE_CLEANUP_FUNCTION(GSList*, lr_local_free_slist, g_slist_free) LR_DEFINE_CLEANUP_FUNCTION(int, lr_local_fd_close, lr_close) /* * g_free() could be used for any pointer type. * To avoid warnings, we must take void * in place of void **. */ static inline void lr_local_free(void *v) { g_free(*(void **)v); } #define _cleanup_dir_close_ __attribute__ ((cleanup(lr_local_dir_close))) #define _cleanup_fd_close_ __attribute__ ((cleanup(lr_local_fd_close))) #define _cleanup_timer_destroy_ __attribute__ ((cleanup(lr_local_destroy_timer))) #define _cleanup_free_ __attribute__ ((cleanup(lr_local_free))) #define _cleanup_checksum_free_ __attribute__ ((cleanup(lr_local_checksum_free))) #define _cleanup_error_free_ __attribute__ ((cleanup(lr_local_free_error))) #define _cleanup_list_free_ __attribute__ ((cleanup(lr_local_free_list))) #define _cleanup_slist_free_ __attribute__ ((cleanup(lr_local_free_slist))) #define _cleanup_string_free_ __attribute__ ((cleanup(lr_local_free_string))) #define _cleanup_strv_free_ __attribute__ ((cleanup(lr_local_strfreev))) #define _cleanup_array_unref_ __attribute__ ((cleanup(lr_local_array_unref))) #define _cleanup_hashtable_unref_ __attribute__ ((cleanup(lr_local_hashtable_unref))) #define _cleanup_keyfile_unref_ __attribute__ ((cleanup(lr_local_keyfile_unref))) #define _cleanup_keyfile_free_ __attribute__ ((cleanup(lr_local_keyfile_free))) #define _cleanup_ptrarray_unref_ __attribute__ ((cleanup(lr_local_ptrarray_unref))) #define _cleanup_date_time_unref_ __attribute__ ((cleanup(lr_local_date_time_unref))) G_END_DECLS #endif librepo-1.12.1/librepo/downloader.c000066400000000000000000003202501372027637700172060ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _XOPEN_SOURCE 500 // Because of fdopen() and ftruncate() #define _DEFAULT_SOURCE // Because of futimes() #define _BSD_SOURCE // Because of futimes() #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WITH_ZCHUNK #include #endif /* WITH_ZCHUNK */ #include "downloader.h" #include "downloader_internal.h" #include "rcodes.h" #include "util.h" #include "downloadtarget.h" #include "downloadtarget_internal.h" #include "handle.h" #include "handle_internal.h" #include "cleanup.h" #include "url_substitution.h" #include "yum_internal.h" #include volatile sig_atomic_t lr_interrupt = 0; void lr_sigint_handler(G_GNUC_UNUSED int sig) { lr_interrupt = 1; } typedef enum { LR_DS_WAITING, /*!< The target is waiting to be processed. */ LR_DS_RUNNING, /*!< The transfer is running. */ LR_DS_FINISHED, /*!< The transfer is successfully finished. */ LR_DS_FAILED, /*!< The transfer is finished without success. */ } LrDownloadState; typedef enum { LR_HCS_DEFAULT, /*!< Default state */ LR_HCS_HTTP_STATE_OK, /*!< HTTP headers with OK state */ LR_HCS_INTERRUPTED, /*!< Download was interrupted (e.g. Content-Length doesn't match expected size etc.) */ LR_HCS_DONE, /*!< All headers which we were looking for are already found*/ } LrHeaderCbState; /** Enum with zchunk file status */ typedef enum { LR_ZCK_DL_HEADER_CK, /*!< The zchunk file is waiting to check whether the header is available locally. */ LR_ZCK_DL_HEADER, /*!< The zchunk file is waiting to download the header */ LR_ZCK_DL_BODY_CK, /*!< The zchunk file is waiting to check what chunks are available locally */ LR_ZCK_DL_BODY, /*!< The zchunk file is waiting for its body to be downloaded. */ LR_ZCK_DL_FINISHED /*!< The zchunk file is finished being downloaded. */ } LrZckState; typedef struct { LrHandle *handle; /*!< Handle (could be NULL) */ GSList *lrmirrors; /*!< List of LrMirrors created from the handle internal mirrorlist (could be NULL) */ } LrHandleMirrors; typedef struct { LrInternalMirror *mirror; /*!< Mirror */ int allowed_parallel_connections; /*!< Maximum number of allowed parallel connections to this mirror. -1 means no limit. Dynamically adjusted (decreased) if no fatal (temporary) error will occur. */ int max_tried_parallel_connections; /*!< The maximum number of tried parallel connections to this mirror (including unsuccessful). */ int running_transfers; /*!< How many transfers from this mirror are currently in progress. */ int successful_transfers; /*!< How many transfers was finished successfully from the mirror. */ int failed_transfers; /*!< How many transfers failed. */ int max_ranges; /*!< Maximum ranges supported in a single request. This will be automatically adjusted when mirrors respond with 200 to a range request */ } LrMirror; typedef struct { LrDownloadState state; /*!< State of the download (transfer). */ LrDownloadTarget *target; /*!< Download target */ LrMirror *mirror; /*!< Mirror is: If a base_location is used then NULL. If state is LR_DS_WAITING then could be NULL (if no download was tried yet) or notNULL if previous download failed, in that case the mirror is the last used mirror (the one that failed). If state is LR_DS_RUNNING then currently used mirror. If state is LR_DS_FINISHED then mirror from which download was successfully performed. If state is LR_DS_FAILED then mirror from which last try was done. */ LrProtocol protocol; /*!< Current protocol */ CURL *curl_handle; /*!< Used curl handle or NULL */ FILE *f; /*!< fdopened file descriptor from LrDownloadTarget and used in curl_handle. */ char errorbuffer[CURL_ERROR_SIZE]; /*!< Error buffer used in curl handle */ GSList *tried_mirrors; /*!< List of already tried mirrors (LrMirror *). This mirrors won't be tried again. */ gboolean resume; /*!< Is resume enabled? Download target may state that resume is True but Librepo can decide that resuming won't be done. This variable states if the resume is enabled or not. */ gint64 original_offset; /*!< If resume is enabled, this is the specified offset where to resume the downloading. If resume is not enabled, then value is -1. */ gint resume_count; /*!< How many resumes were done */ GSList *lrmirrors; /*!< List of all available mirors (LrMirror *). This list is generated from LrHandle related to this target and is common for all targets that uses the handle. */ LrHandle *handle; /*!< LrHandle associated with this target */ LrHeaderCbState headercb_state; /*!< State of the header callback for current transfer */ gchar *headercb_interrupt_reason; /*!< Reason why was the transfer interrupted */ gint64 writecb_recieved; /*!< Total number of bytes received by the write function during the current transfer. */ gboolean writecb_required_range_written; /*!< If a byte range was specified to download and the range was downloaded, it is TRUE. Otherwise FALSE. */ LrCbReturnCode cb_return_code; /*!< Last cb return code. */ struct curl_slist *curl_rqheaders; /*!< Extra headers for request. */ #ifdef WITH_ZCHUNK LrZckState zck_state; /*!< Zchunk download status */ #endif /* WITH_ZCHUNK */ gboolean range_fail; ; /*!< Whether range request failed. */ } LrTarget; typedef struct { // Configuration gboolean failfast; /*!< Fail fast */ int max_parallel_connections; /*!< Maximal number of parallel downloads. */ int max_connection_per_host; /*!< Maximal number of connections per host. -1 means no limit. */ int max_mirrors_to_try; /*!< Maximal number of mirrors to try. Number <= 0 means no limit. */ long allowed_mirror_failures; /*!< See LRO_ALLOWEDMIRRORFAILURES */ long adaptivemirrorsorting; /*!< See LRO_ADAPTIVEMIRRORSORTING */ // Data CURLM *multi_handle; /*!< Curl Multi handle */ GSList *handle_mirrors; /*!< All mirrors (list of pointers to LrHandleMirrors structures) */ GSList *targets; /*!< List of all targets (list of pointers to LrTarget stuctures) */ GSList *running_transfers; /*!< List of running transfers (list of pointer to LrTarget structures) */ } LrDownload; /** Schema of structures as used in downloader module: * * +------------------------------+ * | LrDownload | * +------------------------------+ * | int max_parallel_connections | * | int max_connection_par_host | * | int max_mirrors_to_try | +-------------------+ * | | /->| LrHandleMirrors | * | | | +-------------------+ * | CURLM *multi_handle | | | LrHandle *handle | * | | | | GSList *lrmirrors --\ * | GSList *handle_mirrors ---/ +-------------------+ | * | GSList *targets --\ | * | GSList *running_transfers ---\ | * +------------------------------+ | | * | | * /------------------------------/ | * | | * | /--------------------------------/ * | \/ * | +------------------------------------+ * | | LrMirror | * | +------------------------------------+-| * | | LrMirror | | * | +------------------------------------+-| | * | | LrMirror | | | * | +------------------------------------+ | | +---------------------+ * | | LrInternalMirror *mirror ------------------>| LrInternalMirror | * | | int allowed_parallel_connections | | +---------------------+ * | | int max_tried_parallel_connections |-+ | char *url | * | | int running_transfersers | | int preference | * | | int successful_transfers | | LrProtocol protocol | * | + int failed_transfers +<--\ +---------------------+ * | +------------------------------------+ | * | | * | +----------------------------+ /-----/ * | | LrTarget | | * | +----------------------------+-| | * | | LrTarget | | | +--------------------------+ * | +----------------------------+-| | | /->| LrDownloadTarget | * \-> | LrTarget | | | | | +--------------------------+ * +----------------------------+ | | | | | char *path | * | LrDownloadState state | | | | | | char *baseurl | * | LrDownloadTarget *target -----------/ | int fd | * | LrMirror *mirror --------/ | LrChecksumType checks.. | * | CURL *curl_handle |-+ | char *checksum | * | FILE *f | | int resume | * | GSList *tried_mirrors | | LrProgressCb progresscb | * | gint64 original_offset | | void *cbdata | * | GSlist *lrmirrors ---\ | GStringChunk *chunk | * +----------------------------+ | | int rcode | * | | char *err | * Points to list of LrMirrors <---/ +--------------------------+ */ static gboolean is_max_mirrors_unlimited(const LrDownload *download) { return download->max_mirrors_to_try <= 0; } static gboolean /** * @brief Returns whether the download can be retried, using the same URL in case of baseurl or full * path, or using another mirror in case of using mirrors. * * @param complete_path_or_baseurl determine type of download - mirrors or baseurl/fullpath * @return gboolean Return TRUE when another chance to download is allowed. */ can_retry_download(const LrDownload *download, int num_of_tried_mirrors, const char * complete_path_or_baseurl) { if (complete_path_or_baseurl) { if (g_str_has_prefix(complete_path_or_baseurl, "file:/")) { return FALSE; } return download->allowed_mirror_failures > num_of_tried_mirrors; } return is_max_mirrors_unlimited(download) || num_of_tried_mirrors < download->max_mirrors_to_try; } static gboolean has_running_transfers(const LrMirror *mirror) { return mirror->running_transfers > 0; } static void init_once_allowed_parallel_connections(LrMirror *mirror, int max_allowed_parallel_connections) { if (mirror->allowed_parallel_connections == 0) { mirror->allowed_parallel_connections = max_allowed_parallel_connections; } } static void increase_running_transfers(LrMirror *mirror) { mirror->running_transfers++; if (mirror->max_tried_parallel_connections < mirror->running_transfers) mirror->max_tried_parallel_connections = mirror->running_transfers; } static gboolean is_parallel_connections_limited_and_reached(const LrMirror *mirror) { return mirror->allowed_parallel_connections != -1 && mirror->running_transfers >= mirror->allowed_parallel_connections; } static void mirror_update_statistics(LrMirror *mirror, gboolean transfer_success) { mirror->running_transfers--; if (transfer_success) mirror->successful_transfers++; else mirror->failed_transfers++; } /** Create GSList of LrMirrors (if it doesn't exist) for a handle. * If the list already exists (if more targets use the same handle) * then just set the list to the current target. * If the list doesn't exist yet, create it then create a mapping between * the list and the handle (LrHandleMirrors) and set the list to * the current target. */ static GSList * lr_prepare_lrmirrors(GSList *list, LrTarget *target) { LrHandle *handle = target->handle; for (GSList *elem = list; elem; elem = g_slist_next(elem)) { LrHandleMirrors *handle_mirrors = elem->data; if (handle_mirrors->handle == handle) { // List of LrMirrors for this handle is already created target->lrmirrors = handle_mirrors->lrmirrors; return list; } } GSList *lrmirrors = NULL; if (handle && handle->internal_mirrorlist) { g_debug("%s: Preparing internal mirror list for handle id: %p", __func__, handle); for (GSList *elem = handle->internal_mirrorlist; elem; elem = g_slist_next(elem)) { LrInternalMirror *imirror = elem->data; assert(imirror); assert(imirror->url); if (!imirror || !imirror->url || !strlen(imirror->url)) continue; g_debug("%s: Mirror: %s", __func__, imirror->url); LrMirror *mirror = lr_malloc0(sizeof(*mirror)); mirror->mirror = imirror; mirror->max_ranges = 256; lrmirrors = g_slist_append(lrmirrors, mirror); } } LrHandleMirrors *handle_mirrors = lr_malloc0(sizeof(*handle_mirrors)); handle_mirrors->handle = handle; handle_mirrors->lrmirrors = lrmirrors; target->lrmirrors = lrmirrors; list = g_slist_append(list, handle_mirrors); return list; } /** Progress callback for CURL handles. * progress callback set by the user of librepo. */ static int lr_progresscb(void *ptr, double total_to_download, double now_downloaded, G_GNUC_UNUSED double total_to_upload, G_GNUC_UNUSED double now_uploaded) { int ret = LR_CB_OK; LrTarget *target = ptr; assert(target); assert(target->target); if (target->state != LR_DS_RUNNING) return ret; if (!target->target->progresscb) return ret; #ifdef WITH_ZCHUNK if (target->target->is_zchunk) { total_to_download = target->target->total_to_download; now_downloaded = now_downloaded + target->target->downloaded; } #endif /* WITH_ZCHUNK */ ret = target->target->progresscb(target->target->cbdata, total_to_download, now_downloaded); target->cb_return_code = ret; return ret; } #define STRLEN(s) (sizeof(s)/sizeof(s[0]) - 1) #ifdef WITH_ZCHUNK /* Fail if dl_ctx->fail_no_ranges is set and we get a 200 response */ size_t lr_zckheadercb(char *b, size_t l, size_t c, void *dl_v) { LrTarget *target = (LrTarget *)dl_v; assert(target && target->target); long code = -1; curl_easy_getinfo(target->curl_handle, CURLINFO_RESPONSE_CODE, &code); if(code == 200) { g_debug("%s: Too many ranges were attempted in one download", __func__); target->range_fail = 1; return 0; } return zck_header_cb(b, l, c, target->target->zck_dl); } #endif /* WITH_ZCHUNK */ /** Header callback for CURL handles. * It parses HTTP and FTP headers and try to find length of the content * (file size of the target). If the size is different then the expected * size, then the transfer is interrupted. * This callback is used only if the expected size is specified. */ static size_t lr_headercb(void *ptr, size_t size, size_t nmemb, void *userdata) { assert(userdata); size_t ret = size * nmemb; LrTarget *lrtarget = userdata; LrHeaderCbState state = lrtarget->headercb_state; if (state == LR_HCS_DONE || state == LR_HCS_INTERRUPTED) { // Nothing to do return ret; } #ifdef WITH_ZCHUNK if(lrtarget->target->is_zchunk && lrtarget->mirror->max_ranges > 0) return lr_zckheadercb(ptr, size, nmemb, userdata); #endif /* WITH_ZCHUNK */ char *header = g_strstrip(g_strndup(ptr, size*nmemb)); gint64 expected = lrtarget->target->expectedsize; if (state == LR_HCS_DEFAULT) { if (lrtarget->protocol == LR_PROTOCOL_HTTP && g_str_has_prefix(header, "HTTP/")) { // Header of a HTTP protocol if ((g_strrstr(header, "200") || g_strrstr(header, "206")) && !( g_strrstr(header, "connection established") || g_strrstr(header, "Connection established") || g_strrstr(header, "Connection Established") )) { lrtarget->headercb_state = LR_HCS_HTTP_STATE_OK; } else { // Do nothing (do not change the state) // in case of redirection, 200 OK still could come g_debug("%s: Non OK HTTP header status: %s", __func__, header); } } else if (lrtarget->protocol == LR_PROTOCOL_FTP) { // Headers of a FTP protocol if (g_str_has_prefix(header, "213 ")) { // Code 213 should keep the file size gint64 content_length = g_ascii_strtoll(header+4, NULL, 0); g_debug("%s: Server returned size: \"%s\" " "(converted %"G_GINT64_FORMAT"/%"G_GINT64_FORMAT " expected)", __func__, header+4, content_length, expected); // Compare expected size and size reported by a FTP server if (content_length > 0 && content_length != expected) { g_debug("%s: Size doesn't match (%"G_GINT64_FORMAT " != %"G_GINT64_FORMAT")", __func__, content_length, expected); lrtarget->headercb_state = LR_HCS_INTERRUPTED; lrtarget->headercb_interrupt_reason = g_strdup_printf( "FTP server reports size: %"G_GINT64_FORMAT" " "via 213 code, but expected size is: %"G_GINT64_FORMAT, content_length, expected); ret++; // Return error value } else { lrtarget->headercb_state = LR_HCS_DONE; } } else if (g_str_has_prefix(header, "150")) { // Code 150 should keep the file size // TODO: See parse150 in /usr/lib64/python2.7/ftplib.py } } } if (state == LR_HCS_HTTP_STATE_OK) { if (g_str_has_prefix(header, "Content-Length: ")) { // Content-Length header found char *content_length_str = header + STRLEN("Content-Length: "); gint64 content_length = g_ascii_strtoll(content_length_str, NULL, 0); g_debug("%s: Server returned Content-Length: \"%s\" " "(converted %"G_GINT64_FORMAT"/%"G_GINT64_FORMAT" expected)", __func__, content_length_str, content_length, expected); // Compare expected size and size reported by a HTTP server if (content_length > 0 && content_length != expected) { g_debug("%s: Size doesn't match (%"G_GINT64_FORMAT " != %"G_GINT64_FORMAT")", __func__, content_length, expected); lrtarget->headercb_state = LR_HCS_INTERRUPTED; lrtarget->headercb_interrupt_reason = g_strdup_printf( "Server reports Content-Length: %"G_GINT64_FORMAT" but " "expected size is: %"G_GINT64_FORMAT, content_length, expected); ret++; // Return error value } else { lrtarget->headercb_state = LR_HCS_DONE; } } } g_free(header); return ret; } #ifdef WITH_ZCHUNK /** Zchunk write callback for CURL handles. */ size_t lr_zck_writecb(char *ptr, size_t size, size_t nmemb, void *userdata) { LrTarget *target = (LrTarget *) userdata; if(target->zck_state == LR_ZCK_DL_HEADER) return zck_write_zck_header_cb(ptr, size, nmemb, target->target->zck_dl); else return zck_write_chunk_cb(ptr, size, nmemb, target->target->zck_dl); } #endif /* WITH_ZCHUNK */ /** Write callback for CURL handles. * This callback handles situation when an user wants only specified * byte range of the target file. */ size_t lr_writecb(char *ptr, size_t size, size_t nmemb, void *userdata) { size_t cur_written_expected = nmemb; size_t cur_written; LrTarget *target = (LrTarget *) userdata; #ifdef WITH_ZCHUNK if(target->target->is_zchunk && target->mirror->max_ranges > 0) return lr_zck_writecb(ptr, size, nmemb, userdata); #endif /* WITH_ZCHUNK */ gint64 all = size * nmemb; // Total number of bytes from curl gint64 range_start = target->target->byterangestart; gint64 range_end = target->target->byterangeend; if (range_start <= 0 && range_end <= 0) { // Write everything curl give to you target->writecb_recieved += all; return fwrite(ptr, size, nmemb, target->f); } /* Deal with situation when user wants only specific byte range of the * target file, and write only the range. */ gint64 cur_range_start = target->writecb_recieved; gint64 cur_range_end = cur_range_start + all; target->writecb_recieved += all; if (target->target->byterangestart > 0) { // If byterangestart is specified, then CURLOPT_RESUME_FROM_LARGE // is used by default cur_range_start += target->target->byterangestart; cur_range_end += target->target->byterangestart; } else if (target->original_offset > 0) { cur_range_start += target->original_offset; cur_range_end += target->original_offset; } if (cur_range_end < range_start) // The wanted byte range doesn't start yet return nmemb; if (range_end != 0 && cur_range_start > range_end) { // The wanted byte range is over // Return zero that will lead to transfer abortion // with error code CURLE_WRITE_ERROR target->writecb_required_range_written = TRUE; return 0; } size = 1; nmemb = all; if (cur_range_start >= range_start) { // Write the current curl passed range from the start ; } else { // Find the right starting offset gint64 offset = range_start - cur_range_start; assert(offset > 0); ptr += offset; // Corret the length appropriately nmemb = all - offset; } if (range_end != 0) { // End range is specified if (cur_range_end <= range_end) { // Write the current curl passed range to the end ; } else { // Find the length of the new sequence gint64 offset = cur_range_end - range_end; assert(offset > 0); // Corret the length appropriately nmemb -= (offset - 1); } } assert(nmemb > 0); cur_written = fwrite(ptr, size, nmemb, target->f); if (cur_written != nmemb) { g_warning("Error while writing file: %s", g_strerror(errno)); return 0; // There was an error } return cur_written_expected; } /** Select a suitable mirror */ static gboolean select_suitable_mirror(LrDownload *dd, LrTarget *target, LrMirror **selected_mirror, GError **err) { gboolean at_least_one_suitable_mirror_found = FALSE; // ^^^ This variable is used to indentify that all possible mirrors // were already tried and the transfer should be marked as failed. assert(dd); assert(target); assert(selected_mirror); assert(!err || *err == NULL); *selected_mirror = NULL; // mirrors_iterated is used to allow to use mirrors multiple times for a target unsigned mirrors_iterated = 0; // retry local paths have no reason gboolean reiterate = FALSE; // Iterate over mirrors for the target. If no suitable mirror is found on // the first iteration, relax the conditions (by allowing previously // failing mirrors to be used again) and do additional iterations up to // number of allowed failures equal to dd->allowed_mirror_failures. do { // Iterate over mirror for the target for (GSList *elem = target->lrmirrors; elem; elem = g_slist_next(elem)) { LrMirror *c_mirror = elem->data; gchar *mirrorurl = c_mirror->mirror->url; // first iteration, filter out mirrors that failed previously if (mirrors_iterated == 0) { if (c_mirror->mirror->protocol != LR_PROTOCOL_FILE) reiterate = TRUE; if (g_slist_find(target->tried_mirrors, c_mirror)) { // This mirror was already tried for this target continue; } if (c_mirror->successful_transfers == 0 && dd->allowed_mirror_failures > 0 && c_mirror->failed_transfers >= dd->allowed_mirror_failures) { // Skip bad mirrors g_debug("%s: Skipping bad mirror (%d failures and no success): %s", __func__, c_mirror->failed_transfers, mirrorurl); continue; } // retry of local paths have no reason } else if (c_mirror->mirror->protocol == LR_PROTOCOL_FILE) { continue; // On subsequent iterations, only skip mirrors that failed proportionally to the number // of iterations. It allows to reuse mirrors with low number of failures first. } else if (mirrors_iterated < c_mirror->failed_transfers) { continue; } if (c_mirror->mirror->protocol == LR_PROTOCOL_RSYNC) { if (mirrors_iterated == 0) { // Skip rsync mirrors g_debug("%s: Skipping rsync url: %s", __func__, mirrorurl); } continue; } if (target->handle && target->handle->offline && c_mirror->mirror->protocol != LR_PROTOCOL_FILE) { if (mirrors_iterated == 0) { // Skip each url that doesn't have "file://" or "file:" prefix g_debug("%s: Skipping mirror %s - Offline mode enabled", __func__, mirrorurl); } continue; } at_least_one_suitable_mirror_found = TRUE; // Number of transfers which are downloading from the mirror // should always be lower or equal than maximum allowed number // of connection to a single host. assert(dd->max_connection_per_host == -1 || c_mirror->running_transfers <= dd->max_connection_per_host); // Init max of allowed parallel connections from config init_once_allowed_parallel_connections(c_mirror, dd->max_connection_per_host); // Check number of connections to the mirror if (is_parallel_connections_limited_and_reached(c_mirror)) { continue; } // This mirror looks suitable - use it *selected_mirror = c_mirror; return TRUE; } } while (reiterate && g_slist_length(target->tried_mirrors) < dd->allowed_mirror_failures && ++mirrors_iterated < dd->allowed_mirror_failures); if (!at_least_one_suitable_mirror_found) { // No suitable mirror even exists => Set transfer as failed g_debug("%s: All mirrors were tried without success", __func__); target->state = LR_DS_FAILED; lr_downloadtarget_set_error(target->target, LRE_NOURL, "Cannot download, all mirrors were already tried " "without success"); // Call end callback LrEndCb end_cb = target->target->endcb; if (end_cb) { int ret = end_cb(target->target->cbdata, LR_TRANSFER_ERROR, "No more mirrors to try - All mirrors " "were already tried without success"); if (ret == LR_CB_ERROR) { target->cb_return_code = LR_CB_ERROR; g_debug("%s: Downloading was aborted by LR_CB_ERROR " "from end callback", __func__); g_set_error(err, LR_DOWNLOADER_ERROR, LRE_CBINTERRUPTED, "Interrupted by LR_CB_ERROR from end callback"); return FALSE; } } if (dd->failfast) { // Fail immediately g_set_error(err, LR_DOWNLOADER_ERROR, LRE_NOURL, "Cannot download %s: All mirrors were tried", target->target->path); return FALSE; } } return TRUE; } /** Select next target */ static gboolean select_next_target(LrDownload *dd, LrTarget **selected_target, char **selected_full_url, GError **err) { assert(dd); assert(selected_target); assert(selected_full_url); assert(!err || *err == NULL); *selected_target = NULL; *selected_full_url = NULL; for (GSList *elem = dd->targets; elem; elem = g_slist_next(elem)) { LrTarget *target = elem->data; LrMirror *mirror = NULL; char *full_url = NULL; int complete_url_in_path = 0; if (target->state != LR_DS_WAITING) // Pick only waiting targets continue; // Determine if path is a complete URL complete_url_in_path = strstr(target->target->path, "://") ? 1 : 0; // Sanity check if (!target->target->baseurl && !target->lrmirrors && !complete_url_in_path) { // Used relative path with empty internal mirrorlist // and no basepath specified! g_warning("Empty mirrorlist and no basepath specified"); g_set_error(err, LR_DOWNLOADER_ERROR, LRE_NOURL, "Empty mirrorlist and no basepath specified!"); return FALSE; } g_debug("Selecting mirror for: %s", target->target->path); // Prepare full target URL if (complete_url_in_path) { // Path is a complete URL (do not use mirror nor base URL) full_url = g_strdup(target->target->path); } else if (target->target->baseurl) { // Base URL is specified full_url = lr_pathconcat(target->target->baseurl, target->target->path, NULL); } else { // Find a suitable mirror if (!select_suitable_mirror(dd, target, &mirror , err)) return FALSE; if (mirror) { // A mirror was found full_url = lr_pathconcat(mirror->mirror->url, target->target->path, NULL); } else { // No free mirror g_debug("%s: Currently there is no free mirror for: %s", __func__, target->target->path); } } // If LRO_OFFLINE is specified, check if the obtained full_url // is local or not // This condition should never be true for a full_url built // from a mirror, because select_suitable_mirror() checks if // the URL is local if LRO_OFFLINE is enabled by itself. if (full_url && target->handle && target->handle->offline && !lr_is_local_path(full_url)) { g_debug("%s: Skipping %s because LRO_OFFLINE is specified", __func__, full_url); // Mark the target as failed target->state = LR_DS_FAILED; lr_downloadtarget_set_error(target->target, LRE_NOURL, "Cannot download, offline mode is specified and no " "local URL is available"); // Call end callback LrEndCb end_cb = target->target->endcb; if (end_cb) { int ret = end_cb(target->target->cbdata, LR_TRANSFER_ERROR, "Cannot download: Offline mode is specified " "and no local URL is available"); if (ret == LR_CB_ERROR) { target->cb_return_code = LR_CB_ERROR; g_debug("%s: Downloading was aborted by LR_CB_ERROR " "from end callback", __func__); g_set_error(err, LR_DOWNLOADER_ERROR, LRE_CBINTERRUPTED, "Interrupted by LR_CB_ERROR from end callback"); return FALSE; } } if (dd->failfast) { // Fail immediately g_set_error(err, LR_DOWNLOADER_ERROR, LRE_NOURL, "Cannot download %s: Offline mode is specified " "and no local URL is available", target->target->path); return FALSE; } } if (full_url) { // A waiting target found target->mirror = mirror; // Note: mirror is NULL if baseurl is used *selected_target = target; *selected_full_url = full_url; return TRUE; } } // No suitable target found return TRUE; } #define XATTR_LIBREPO "user.Librepo.DownloadInProgress" /** Add an extended attribute that indicates that * the file is being/was downloaded by Librepo */ static void add_librepo_xattr(int fd, const gchar *fn) { _cleanup_free_ gchar *dst = NULL; if (!fn) dst = g_strdup_printf("fd: %d", fd); else dst = g_strdup(fn); #if __APPLE__ int attr_ret = fsetxattr(fd, XATTR_LIBREPO, "1", 1, 0, 0); #else int attr_ret = fsetxattr(fd, XATTR_LIBREPO, "1", 1, 0); #endif if (attr_ret == -1) { g_debug("%s: Cannot set xattr %s (%s): %s", __func__, XATTR_LIBREPO, dst, g_strerror(errno)); } } /** Check if the file was downloaded by Librepo */ static gboolean has_librepo_xattr(int fd) { #if __APPLE__ ssize_t attr_ret = fgetxattr(fd, XATTR_LIBREPO, NULL, 0, 0, 0); #else ssize_t attr_ret = fgetxattr(fd, XATTR_LIBREPO, NULL, 0); #endif if (attr_ret == -1) { //g_debug("%s: Cannot get xattr %s: %s", // __func__, XATTR_LIBREPO, g_strerror(errno)); return FALSE; } return TRUE; } /** Remove Librepo extended attribute */ static void remove_librepo_xattr(LrDownloadTarget * target) { int fd = target->fd; if (fd != -1) { #if __APPLE__ fremovexattr(fd, XATTR_LIBREPO, 0); #else fremovexattr(fd, XATTR_LIBREPO); #endif return; } // If file descriptor wasn't set, file name was, and we need to open it fd = open(target->fn, O_RDWR, 0666); if (fd == -1) { return; } #if __APPLE__ fremovexattr(fd, XATTR_LIBREPO, 0); #else fremovexattr(fd, XATTR_LIBREPO); #endif close(fd); } #ifdef WITH_ZCHUNK gboolean lr_zck_clear_header(LrTarget *target, GError **err) { assert(target && target->f && target->target && target->target->path); int fd = fileno(target->f); lseek(fd, 0, SEEK_END); if(ftruncate(fd, 0) < 0) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_IO, "Unable to truncate %s", target->target->path); return FALSE; } else { return TRUE; } } static gboolean find_local_zck_header(LrTarget *target, GError **err) { zckCtx *zck = NULL; gboolean found = FALSE; int fd = fileno(target->f); if(target->target->handle->cachedir) { g_debug("%s: Cache directory: %s\n", __func__, target->handle->cachedir); GError *tmp_err = NULL; GSList *filelist = lr_get_recursive_files(target->handle->cachedir, ".zck", &tmp_err); if(tmp_err) { g_warning("Error reading cache directory %s: %s", target->handle->cachedir, tmp_err->message); g_clear_error(&tmp_err); } char *uf = g_build_path("/", target->handle->destdir, target->target->path, NULL); for(GSList *file = filelist; file && !found; file = g_slist_next(file)) { char *cf = (char *)file->data; /* Don't try to read from self */ if (strcmp(cf, uf) == 0) continue; int chk_fd = open(file->data, O_RDONLY); if (chk_fd < 0) { g_warning("Unable to open %s: %s", cf, g_strerror(errno)); continue; } if(lr_zck_valid_header(target->target, (char *)file->data, chk_fd, &tmp_err)) { g_debug("%s: Found file with same header at %s", __func__, (char *)file->data); lseek(fd, 0, SEEK_SET); lseek(chk_fd, 0, SEEK_SET); if(lr_copy_content(chk_fd, fd) == 0 && ftruncate(fd, lseek(chk_fd, 0, SEEK_END)) >= 0 && lseek(fd, 0, SEEK_SET) == 0 && (zck = lr_zck_init_read(target->target, (char *)file->data, chk_fd, &tmp_err))) { found = TRUE; break; } else { g_warning("Error copying file"); g_clear_error(&tmp_err); } } else { g_clear_error(&tmp_err); } close(chk_fd); } g_slist_free_full(filelist, free); free(uf); } else { g_debug("%s: No cache directory set", __func__); } if(found) { zckCtx *old_zck = zck_dl_get_zck(target->target->zck_dl); zck_free(&old_zck); if(!zck_dl_set_zck(target->target->zck_dl, zck)) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK, "Unable to setup zchunk download context for %s", target->target->path); return FALSE; } target->zck_state = LR_ZCK_DL_BODY_CK; return TRUE; } target->zck_state = LR_ZCK_DL_HEADER; return TRUE; } static gboolean prep_zck_header(LrTarget *target, GError **err) { zckCtx *zck = NULL; int fd = fileno(target->f); GError *tmp_err = NULL; if(lr_zck_valid_header(target->target, target->target->path, fd, &tmp_err)) { zck = lr_zck_init_read(target->target, target->target->path, fd, &tmp_err); if(zck) { if(!zck_dl_set_zck(target->target->zck_dl, zck)) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK, "Unable to setup zchunk download context"); return FALSE; } target->zck_state = LR_ZCK_DL_BODY_CK; return TRUE; } else { g_warning("Error reading validated header: %s", tmp_err->message); g_clear_error(&tmp_err); } } else { g_clear_error(&tmp_err); } lseek(fd, 0, SEEK_SET); zck = zck_create(); if(!zck_init_adv_read(zck, fd)) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK, "Unable to initialize zchunk file %s for reading", target->target->path); return FALSE; } if(target->target->zck_dl) { zckCtx *old_zck = zck_dl_get_zck(target->target->zck_dl); zck_free(&old_zck); if(!zck_dl_set_zck(target->target->zck_dl, zck)) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK, "Unable to setup zchunk download context for %s", target->target->path); return FALSE; } } else { target->target->zck_dl = zck_dl_init(zck); } target->target->range = zck_get_range(0, target->target->zck_header_size-1); target->target->total_to_download = target->target->zck_header_size; target->target->resume = 0; target->zck_state = LR_ZCK_DL_HEADER; return lr_zck_clear_header(target, err); } static gboolean find_local_zck_chunks(LrTarget *target, GError **err) { assert(!err || *err == NULL); assert(target && target->target && target->target->zck_dl); zckCtx *zck = zck_dl_get_zck(target->target->zck_dl); int fd = fileno(target->f); if(zck && fd != zck_get_fd(zck) && !zck_set_fd(zck, fd)) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK, "Unable to set zchunk file descriptor for %s: %s", target->target->path, zck_get_error(zck)); return FALSE; } if(target->target->handle->cachedir) { g_debug("%s: Cache directory: %s\n", __func__, target->handle->cachedir); GError *tmp_err = NULL; GSList *filelist = lr_get_recursive_files(target->handle->cachedir, ".zck", &tmp_err); if(tmp_err) { g_warning("Error reading cache directory %s: %s", target->handle->cachedir, tmp_err->message); g_clear_error(&tmp_err); } gboolean found = FALSE; char *uf = g_build_path("/", target->handle->destdir, target->target->path, NULL); for(GSList *file = filelist; file && !found; file = g_slist_next(file)) { char *cf = (char *)file->data; /* Don't try to read from self */ if (strcmp(cf, uf) == 0) continue; int chk_fd = open(file->data, O_RDONLY); if (chk_fd < 0) { g_warning("Unable to open %s: %s", cf, g_strerror(errno)); continue; } zckCtx *zck_src = zck_create(); if(!zck_init_read(zck_src, chk_fd)) { close(chk_fd); continue; } if(!zck_copy_chunks(zck_src, zck)) { g_warning("Error copying chunks from %s to %s: %s", cf, uf, zck_get_error(zck)); zck_free(&zck_src); close(chk_fd); continue; } zck_free(&zck_src); close(chk_fd); } g_slist_free_full(filelist, free); free(uf); } target->target->downloaded = target->target->total_to_download; /* Calculate how many bytes need to be downloaded */ for(zckChunk *idx = zck_get_first_chunk(zck); idx != NULL; idx = zck_get_next_chunk(idx)) if(zck_get_chunk_valid(idx) != 1) target->target->total_to_download += zck_get_chunk_comp_size(idx) + 92; /* Estimate of multipart overhead */ target->zck_state = LR_ZCK_DL_BODY; return TRUE; } static gboolean prep_zck_body(LrTarget *target, GError **err) { zckCtx *zck = zck_dl_get_zck(target->target->zck_dl); int fd = fileno(target->f); if(zck && fd != zck_get_fd(zck) && !zck_set_fd(zck, fd)) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK, "Unable to set zchunk file descriptor for %s: %s", target->target->path, zck_get_error(zck)); return FALSE; } zck_reset_failed_chunks(zck); if(zck_missing_chunks(zck) == 0) { target->zck_state = LR_ZCK_DL_FINISHED; return TRUE; } lseek(fd, 0, SEEK_SET); g_debug("%s: Chunks that still need to be downloaded: %i", __func__, zck_missing_chunks(zck)); zck_dl_reset(target->target->zck_dl); zckRange *range = zck_get_missing_range(zck, target->mirror->max_ranges); zckRange *old_range = zck_dl_get_range(target->target->zck_dl); if(old_range) zck_range_free(&old_range); if(!zck_dl_set_range(target->target->zck_dl, range)) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK, "Unable to set range for zchunk downloader"); return FALSE; } if(target->target->range) free(target->target->range); target->target->range = zck_get_range_char(zck, range); target->target->expectedsize = 1; target->zck_state = LR_ZCK_DL_BODY; return TRUE; } static gboolean check_zck(LrTarget *target, GError **err) { assert(!err || *err == NULL); assert(target && target->f && target->target); if(target->mirror->max_ranges == 0) { target->zck_state = LR_ZCK_DL_BODY; target->target->expectedsize = target->target->origsize; return TRUE; } if(target->target->zck_dl == NULL) { target->target->zck_dl = zck_dl_init(NULL); if(target->target->zck_dl == NULL) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK, "%s", zck_get_error(NULL)); return FALSE; } target->zck_state = LR_ZCK_DL_HEADER_CK; } /* Reset range fail flag */ target->range_fail = FALSE; /* If we've finished, then there's no point in checking any further */ if(target->zck_state == LR_ZCK_DL_FINISHED) return TRUE; zckCtx *zck = zck_dl_get_zck(target->target->zck_dl); if (!zck) { target->zck_state = LR_ZCK_DL_HEADER_CK; g_debug("%s: Unable to read zchunk header: %s", __func__, target->target->path); if(!find_local_zck_header(target, err)) return FALSE; } zck = zck_dl_get_zck(target->target->zck_dl); if(target->zck_state == LR_ZCK_DL_HEADER) { if(!prep_zck_header(target, err)) return FALSE; if(target->zck_state == LR_ZCK_DL_HEADER) return TRUE; } zck = zck_dl_get_zck(target->target->zck_dl); if(target->zck_state == LR_ZCK_DL_BODY_CK) { g_debug("%s: Checking zchunk data checksum: %s", __func__, target->target->path); // Check whether file has been fully downloaded int cks_good = zck_find_valid_chunks(zck); if(!cks_good) { // Error while validating checksums g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK, "%s: Error validating zchunk file: %s", __func__, zck_get_error(zck)); return FALSE; } if(cks_good == 1) { // All checksums good g_debug("%s: File is complete", __func__); if(target->target->zck_dl) zck_dl_free(&(target->target->zck_dl)); target->zck_state = LR_ZCK_DL_FINISHED; return TRUE; } g_debug("%s: Downloading rest of zchunk body: %s", __func__, target->target->path); // Download the remaining checksums zck_reset_failed_chunks(zck); if(!find_local_zck_chunks(target, err)) return FALSE; cks_good = zck_find_valid_chunks(zck); if(!cks_good) { // Error while validating checksums g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK, "%s: Error validating zchunk file: %s", __func__, zck_get_error(zck)); return FALSE; } if(cks_good == 1) { // All checksums good if(target->target->zck_dl) zck_dl_free(&(target->target->zck_dl)); target->zck_state = LR_ZCK_DL_FINISHED; return TRUE; } } zck_reset_failed_chunks(zck); /* Recalculate how many bytes remain to be downloaded by subtracting from total_to_download */ target->target->downloaded = target->target->total_to_download; for(zckChunk *idx = zck_get_first_chunk(zck); idx != NULL; idx = zck_get_next_chunk(idx)) if(zck_get_chunk_valid(idx) != 1) target->target->downloaded -= zck_get_chunk_comp_size(idx) + 92; return prep_zck_body(target, err); } #endif /* WITH_ZCHUNK */ /** Open the file to write to */ static FILE* open_target_file(LrTarget *target, GError **err) { int fd; FILE *f; if (target->target->fd != -1) { // Use supplied filedescriptor fd = dup(target->target->fd); if (fd == -1) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_IO, "dup(%d) failed: %s", target->target->fd, g_strerror(errno)); return NULL; } } else { // Use supplied filename int open_flags = O_CREAT|O_TRUNC|O_RDWR; if (target->resume || target->target->is_zchunk) open_flags &= ~O_TRUNC; fd = open(target->target->fn, open_flags, 0666); if (fd == -1) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_IO, "Cannot open %s: %s", target->target->fn, g_strerror(errno)); return NULL; } } f = fdopen(fd, "w+b"); if (!f) { close(fd); g_set_error(err, LR_DOWNLOADER_ERROR, LRE_IO, "fdopen(%d) failed: %s", fd, g_strerror(errno)); return NULL; } return f; } /** Prepare next transfer */ static gboolean prepare_next_transfer(LrDownload *dd, gboolean *candidatefound, GError **err) { LrTarget *target; _cleanup_free_ char *full_url = NULL; LrProtocol protocol = LR_PROTOCOL_OTHER; gboolean ret; assert(dd); assert(!err || *err == NULL); *candidatefound = FALSE; ret = select_next_target(dd, &target, &full_url, err); if (!ret) // Error return FALSE; if (!target) // Nothing to do return TRUE; *candidatefound = TRUE; // Append the LRO_ONETIMEFLAG if instructed to do so LrHandle *handle = target->handle; if (handle && handle->onetimeflag && handle->onetimeflag_apply) { const char *sep = "?"; if (g_strrstr(full_url, sep) != NULL) sep = "&"; char *new_url = g_strjoin(sep, full_url, handle->onetimeflag, NULL); g_free(full_url); full_url = new_url; // No other CURL handle on this LrHandle shall apply the flag again free(handle->onetimeflag); handle->onetimeflag = NULL; handle->onetimeflag_apply = FALSE; } g_info("Downloading: %s", full_url); protocol = lr_detect_protocol(full_url); // Prepare CURL easy handle CURLcode c_rc; CURL *h; if (target->handle) h = curl_easy_duphandle(target->handle->curl_handle); else h = lr_get_curl_handle(); if (!h) { // Something went wrong g_set_error(err, LR_DOWNLOADER_ERROR, LRE_CURL, "curl_easy_duphandle() call failed"); goto fail; } target->curl_handle = h; // Set URL c_rc = curl_easy_setopt(h, CURLOPT_URL, full_url); if (c_rc != CURLE_OK) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_CURL, "curl_easy_setopt(h, CURLOPT_URL, %s) failed: %s", full_url, curl_easy_strerror(c_rc)); goto fail; } // Set error buffer target->errorbuffer[0] = '\0'; c_rc = curl_easy_setopt(h, CURLOPT_ERRORBUFFER, target->errorbuffer); if (c_rc != CURLE_OK) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_CURL, "curl_easy_setopt(h, CURLOPT_ERRORBUFFER, target->errorbuffer) failed: %s", curl_easy_strerror(c_rc)); goto fail; } // Prepare FILE target->f = open_target_file(target, err); if (!target->f) goto fail; target->writecb_recieved = 0; target->writecb_required_range_written = FALSE; #ifdef WITH_ZCHUNK // If file is zchunk, prep it if(target->target->is_zchunk) { GError *tmp_err = NULL; if (!check_zck(target, &tmp_err)) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK, "Unable to initialize zchunk file %s: %s", target->target->path, tmp_err->message); goto fail; } // If zchunk is finished, we're done, so move to next target if(target->zck_state == LR_ZCK_DL_FINISHED) { g_debug("%s: Target already fully downloaded: %s", __func__, target->target->path); target->state = LR_DS_FINISHED; curl_easy_cleanup(target->curl_handle); target->curl_handle = NULL; g_free(target->headercb_interrupt_reason); target->headercb_interrupt_reason = NULL; fclose(target->f); target->f = NULL; lr_downloadtarget_set_error(target->target, LRE_OK, NULL); return prepare_next_transfer(dd, candidatefound, err); } } # endif /* WITH_ZCHUNK */ int fd = fileno(target->f); // Allow resume only for files that were originally being // downloaded by librepo if (target->resume && !has_librepo_xattr(fd)) { target->resume = FALSE; g_debug("%s: Resume ignored, existing file was not originally " "being downloaded by Librepo", __func__); if (ftruncate(fd, 0) == -1) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_IO, "ftruncate() failed: %s", g_strerror(errno)); goto fail; } } if (target->resume && target->resume_count >= LR_DOWNLOADER_MAXIMAL_RESUME_COUNT) { target->resume = FALSE; g_debug("%s: Download resume ignored, maximal number of attempts (%d)" " has been reached", __func__, LR_DOWNLOADER_MAXIMAL_RESUME_COUNT); } // Resume - set offset to resume incomplete download if (target->resume) { target->resume_count++; if (target->original_offset == -1) { // Determine offset fseek(target->f, 0L, SEEK_END); gint64 determined_offset = ftell(target->f); if (determined_offset == -1) { // An error while determining offset => // Download the whole file again determined_offset = 0; } target->original_offset = determined_offset; } gint64 used_offset = target->original_offset; g_debug("%s: Used offset for download resume: %"G_GINT64_FORMAT, __func__, used_offset); c_rc = curl_easy_setopt(h, CURLOPT_RESUME_FROM_LARGE, (curl_off_t) used_offset); assert(c_rc == CURLE_OK); } // Add librepo extended attribute to the file // This xattr states that file is being downloaded by librepo // This xattr is removed once the file is completely downloaded // If librepo tries to resume a download, it checks if the xattr is present. // If it isn't the download is not resumed, but whole file is // downloaded again. add_librepo_xattr(fd, target->target->fn); if (target->target->byterangestart > 0) { assert(!target->target->resume && !target->target->range); g_debug("%s: byterangestart is specified -> resume is set to %" G_GINT64_FORMAT, __func__, target->target->byterangestart); c_rc = curl_easy_setopt(h, CURLOPT_RESUME_FROM_LARGE, (curl_off_t) target->target->byterangestart); assert(c_rc == CURLE_OK); } // Set range if user specified one if (target->target->range) { assert(!target->target->resume && !target->target->byterangestart); c_rc = curl_easy_setopt(h, CURLOPT_RANGE, target->target->range); assert(c_rc == CURLE_OK); } // Prepare progress callback target->cb_return_code = LR_CB_OK; if (target->target->progresscb) { c_rc = curl_easy_setopt(h, CURLOPT_PROGRESSFUNCTION, lr_progresscb) || curl_easy_setopt(h, CURLOPT_NOPROGRESS, 0) || curl_easy_setopt(h, CURLOPT_PROGRESSDATA, target); assert(c_rc == CURLE_OK); } // Prepare header callback if (target->target->expectedsize > 0) { c_rc = curl_easy_setopt(h, CURLOPT_HEADERFUNCTION, lr_headercb) || curl_easy_setopt(h, CURLOPT_HEADERDATA, target); assert(c_rc == CURLE_OK); } // Prepare write callback c_rc = curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, lr_writecb) || curl_easy_setopt(h, CURLOPT_WRITEDATA, target); assert(c_rc == CURLE_OK); // Set extra HTTP headers struct curl_slist *headers = NULL; if (target->handle && target->handle->httpheader) { // Fill in headers specified by user in LrHandle via LRO_HTTPHEADER for (int x=0; target->handle->httpheader[x]; x++) { headers = curl_slist_append(headers, target->handle->httpheader[x]); if (!headers) lr_out_of_memory(); } } if (target->target->no_cache) { // Add headers that tell proxy to serve us fresh data headers = curl_slist_append(headers, "Cache-Control: no-cache"); headers = curl_slist_append(headers, "Pragma: no-cache"); if (!headers) lr_out_of_memory(); } target->curl_rqheaders = headers; c_rc = curl_easy_setopt(h, CURLOPT_HTTPHEADER, headers); assert(c_rc == CURLE_OK); // Add the new handle to the curl multi handle CURLMcode cm_rc = curl_multi_add_handle(dd->multi_handle, h); assert(cm_rc == CURLM_OK); // Set the state of transfer as running target->state = LR_DS_RUNNING; // Increase running transfers counter for mirror if (target->mirror) { increase_running_transfers(target->mirror); } // Set the state of header callback for this transfer target->headercb_state = LR_HCS_DEFAULT; g_free(target->headercb_interrupt_reason); target->headercb_interrupt_reason = NULL; // Set protocol of the target target->protocol = protocol; // Add the transfer to the list of running transfers dd->running_transfers = g_slist_append(dd->running_transfers, target); return TRUE; fail: // Cleanup target if (target->curl_handle) { curl_easy_cleanup(target->curl_handle); target->curl_handle = NULL; } if (target->f != NULL) { fclose(target->f); target->f = NULL; } return FALSE; } static gboolean set_max_speeds_to_transfers(LrDownload *dd, GError **err) { assert(!err || *err == NULL); if (!g_slist_length(dd->running_transfers)) // Nothing to do return TRUE; // Compute number of running downloads from repos with limited speed GHashTable *num_running_downloads_per_repo = g_hash_table_new(NULL, NULL); for (GSList *elem = dd->running_transfers; elem; elem = g_slist_next(elem)) { const LrTarget *ltarget = elem->data; if (!ltarget->handle || !ltarget->handle->maxspeed) // Skip repos with unlimited speed or without handle continue; guint num_running_downloads_from_repo = GPOINTER_TO_UINT(g_hash_table_lookup(num_running_downloads_per_repo, ltarget->handle)); if (num_running_downloads_from_repo) ++num_running_downloads_from_repo; else num_running_downloads_from_repo = 1; g_hash_table_insert(num_running_downloads_per_repo, ltarget->handle, GUINT_TO_POINTER(num_running_downloads_from_repo)); } // Set max speed to transfers GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, num_running_downloads_per_repo); while (g_hash_table_iter_next(&iter, &key, &value)) { const LrHandle *repo = key; const guint num_running_downloads_from_repo = GPOINTER_TO_UINT(value); // Calculate a max speed (rounded up) per target (for repo) const gint64 single_target_speed = (repo->maxspeed + (num_running_downloads_from_repo - 1)) / num_running_downloads_from_repo; for (GSList *elem = dd->running_transfers; elem; elem = g_slist_next(elem)) { LrTarget *ltarget = elem->data; if (ltarget->handle == repo) { CURL *curl_handle = ltarget->curl_handle; CURLcode code = curl_easy_setopt(curl_handle, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t)single_target_speed); if (code != CURLE_OK) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_CURLSETOPT, "Cannot set CURLOPT_MAX_RECV_SPEED_LARGE option: %s", curl_easy_strerror(code)); g_hash_table_destroy(num_running_downloads_per_repo); return FALSE; } } } } g_hash_table_destroy(num_running_downloads_per_repo); return TRUE; } static gboolean prepare_next_transfers(LrDownload *dd, GError **err) { guint length = g_slist_length(dd->running_transfers); guint free_slots = dd->max_parallel_connections - length; assert(!err || *err == NULL); while (free_slots > 0) { gboolean candidatefound; if (!prepare_next_transfer(dd, &candidatefound, err)) return FALSE; if (!candidatefound) break; free_slots--; } // Set maximal speed for each target if (!set_max_speeds_to_transfers(dd, err)) return FALSE; return TRUE; } /** Check the finished transfer * Evaluate CURL return code and status code of protocol if needed. * @param serious_error Serious error is an error that isn't fatal, * but mirror that generate it should be penalized. * E.g.: Connection timeout - a mirror we are unable * to connect at is pretty useless for us, but * this could be only temporary state. * No fatal but also no good. * @param fatal_error An error that cannot be recovered - e.g. * we cannot write to a socket, we cannot write * data to disk, bad function argument, ... */ static gboolean check_finished_transfer_status(CURLMsg *msg, LrTarget *target, gboolean *serious_error, gboolean *fatal_error, GError **transfer_err, GError **err) { long code = 0; char *effective_url = NULL; assert(msg); assert(target); assert(!transfer_err || *transfer_err == NULL); assert(!err || *err == NULL); *fatal_error = FALSE; curl_easy_getinfo(msg->easy_handle, CURLINFO_EFFECTIVE_URL, &effective_url); if (msg->data.result != CURLE_OK) { // There was an error that is reported by CURLcode if (msg->data.result == CURLE_WRITE_ERROR && target->writecb_required_range_written) { // Download was interrupted by writecb because // user want only specified byte range of the // target and the range was already downloaded g_debug("%s: Transfer was interrupted by writecb() " "because the required range " "(%"G_GINT64_FORMAT"-%"G_GINT64_FORMAT") " "was downloaded.", __func__, target->target->byterangestart, target->target->byterangeend); } else if (target->headercb_state == LR_HCS_INTERRUPTED) { // Download was interrupted by header callback g_set_error(transfer_err, LR_DOWNLOADER_ERROR, LRE_CURL, "Interrupted by header callback: %s", target->headercb_interrupt_reason); } #ifdef WITH_ZCHUNK else if (target->range_fail) { zckRange *range = zck_dl_get_range(target->target->zck_dl); int range_count = zck_get_range_count(range); if(target->mirror->max_ranges >= range_count) { target->mirror->max_ranges = range_count / 2; g_debug("%s: Setting mirror's max_ranges to %i", __func__, target->mirror->max_ranges); } return TRUE; } else if (target->target->zck_dl != NULL && zck_is_error(zck_dl_get_zck(target->target->zck_dl)) > 0) { zckCtx *zck = zck_dl_get_zck(target->target->zck_dl); // Something went wrong while writing the zchunk file g_set_error(transfer_err, LR_DOWNLOADER_ERROR, LRE_ZCK, "Zchunk error: %s", zck_get_error(zck)); if (zck_is_error(zck) == 1) { // Non-fatal zchunk error g_info("Serious zchunk error: %s", zck_get_error(zck)); *serious_error = TRUE; } else { // zck_is_error(zck) == 2 // Fatal zchunk error g_info("Fatal zchunk error: %s", zck_get_error(zck)); *fatal_error = TRUE; } return TRUE; } #endif /* WITH_ZCHUNK */ else { // There was a CURL error g_set_error(transfer_err, LR_DOWNLOADER_ERROR, LRE_CURL, "Curl error (%d): %s for %s [%s]", msg->data.result, curl_easy_strerror(msg->data.result), effective_url, target->errorbuffer); switch (msg->data.result) { case CURLE_ABORTED_BY_CALLBACK: case CURLE_BAD_FUNCTION_ARGUMENT: case CURLE_CONV_REQD: case CURLE_COULDNT_RESOLVE_PROXY: case CURLE_FILESIZE_EXCEEDED: case CURLE_INTERFACE_FAILED: #if LR_CURL_VERSION_CHECK(7, 21, 5) case CURLE_NOT_BUILT_IN: #endif case CURLE_OUT_OF_MEMORY: //case CURLE_RECV_ERROR: // See RhBug: 1219817 //case CURLE_SEND_ERROR: case CURLE_SSL_CACERT_BADFILE: case CURLE_SSL_CRL_BADFILE: case CURLE_WRITE_ERROR: // Fatal error g_info("Fatal error - Curl code (%d): %s for %s [%s]", msg->data.result, curl_easy_strerror(msg->data.result), effective_url, target->errorbuffer); *fatal_error = TRUE; break; case CURLE_OPERATION_TIMEDOUT: // Serious error g_info("Serious error - Curl code (%d): %s for %s [%s]", msg->data.result, curl_easy_strerror(msg->data.result), effective_url, target->errorbuffer); *serious_error = TRUE; break; default: // Other error are not considered fatal break; } } return TRUE; } // curl return code is CURLE_OK but we need to check status code curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &code); if (code) { char * effective_ip = NULL; curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIMARY_IP, &effective_ip); // Check status codes for some protocols if (effective_url && g_str_has_prefix(effective_url, "http")) { // Check HTTP(S) code if (code/100 != 2) { g_set_error(transfer_err, LR_DOWNLOADER_ERROR, LRE_BADSTATUS, "Status code: %ld for %s (IP: %s)", code, effective_url, effective_ip); } } else if (effective_url) { // Check FTP if (code/100 != 2) { g_set_error(transfer_err, LR_DOWNLOADER_ERROR, LRE_BADSTATUS, "Status code: %ld for %s (IP: %s)", code, effective_url, effective_ip); } } else { // Other protocols g_set_error(transfer_err, LR_DOWNLOADER_ERROR, LRE_BADSTATUS, "Status code: %ld for %s (IP: %s)", code, effective_url, effective_ip); } } return TRUE; } static gchar * list_of_checksums_to_str(GSList *checksums) { if (!checksums) return NULL; gchar *expected = g_strdup(""); // Prepare pretty messages with list of expected checksums for (GSList *elem = checksums; elem; elem = g_slist_next(elem)) { gchar *tmp = NULL; LrDownloadTargetChecksum *chksum = elem->data; if (!chksum || !chksum->value || chksum->type == LR_CHECKSUM_UNKNOWN) continue; // Bad checksum const gchar *chtype_str = lr_checksum_type_to_str(chksum->type); tmp = g_strconcat(expected, chksum->value, "(", chtype_str ? chtype_str : "UNKNOWN", ") ", NULL); free(expected); expected = tmp; } return expected; } static gboolean check_finished_transfer_checksum(int fd, GSList *checksums, gboolean *checksum_matches, GError **transfer_err, GError **err) { gboolean ret = TRUE; gboolean matches = TRUE; GSList *calculated_chksums = NULL; for (GSList *elem = checksums; elem; elem = g_slist_next(elem)) { LrDownloadTargetChecksum *chksum = elem->data; LrDownloadTargetChecksum *calculated_chksum = NULL; gchar *calculated = NULL; if (!chksum || !chksum->value || chksum->type == LR_CHECKSUM_UNKNOWN) continue; // Bad checksum lseek(fd, 0, SEEK_SET); ret = lr_checksum_fd_compare(chksum->type, fd, chksum->value, 1, &matches, &calculated, err); if (!ret) goto cleanup; // Store calculated checksum calculated_chksum = lr_downloadtargetchecksum_new(chksum->type, calculated); g_free(calculated); calculated_chksums = g_slist_append(calculated_chksums, calculated_chksum); if (matches) { // At least one checksum matches g_debug("%s: Checksum (%s) %s is OK", __func__, lr_checksum_type_to_str(chksum->type), chksum->value); break; } } *checksum_matches = matches; if (!matches) { // Checksums doesn't match _cleanup_free_ gchar *calculated = NULL; _cleanup_free_ gchar *expected = NULL; calculated = list_of_checksums_to_str(calculated_chksums); expected = list_of_checksums_to_str(checksums); // Set error message g_set_error(transfer_err, LR_DOWNLOADER_ERROR, LRE_BADCHECKSUM, "Downloading successful, but checksum doesn't match. " "Calculated: %s Expected: %s", calculated, expected); } cleanup: g_slist_free_full(calculated_chksums, (GDestroyNotify) lr_downloadtargetchecksum_free); return ret; } /** Truncate file - Used to remove downloaded garbage (error html pages, etc.) */ static gboolean truncate_transfer_file(LrTarget *target, GError **err) { off_t original_offset = 0; // Truncate whole file by default int rc; assert(!err || *err == NULL); if (target->original_offset > -1) // If resume is enabled -> truncate file to its original position original_offset = target->original_offset; if (target->target->fn) // Truncate by filename rc = truncate(target->target->fn, original_offset); else // Truncate by file descriptor number rc = ftruncate(target->target->fd, original_offset); if (rc == -1) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_IO, "ftruncate() failed: %s", g_strerror(errno)); return FALSE; } if (!target->target->fn) { // In case fd is used, seek to the original offset if (lseek(target->target->fd, original_offset, SEEK_SET) == -1) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_IO, "lseek() failed: %s", g_strerror(errno)); return FALSE; } } return TRUE; } /** Return mirror rank or -1.0 if the rank cannot be determined * (e.g. when is too early) * Rank is currently just success rate for the mirror */ static gdouble mirror_rank(LrMirror *mirror) { gdouble rank = -1.0; int successful = mirror->successful_transfers; int failed = mirror->failed_transfers; int finished_transfers = successful + failed; if (finished_transfers < 3) return rank; // Do not judge too early rank = successful / (double) finished_transfers; return rank; } /** Sort mirrors. Penalize the error ones. * In fact only move the current finished mirror forward or backward * by one position. * @param mirrors GSList of mirrors (order of list elements won't be * changed, only data pointers) * @param mirror Mirror of just finished transfer * @param success Was download from the mirror successful * @param serious If success is FALSE, serious mean that error was serious * (like connection timeout), and the mirror should be * penalized more that usual. */ static gboolean sort_mirrors(GSList *mirrors, LrMirror *mirror, gboolean success, gboolean serious) { GSList *elem = mirrors; GSList *prev = NULL; GSList *next = NULL; gdouble rank_cur; assert(mirrors); assert(mirror); for (; elem && elem->data != mirror; elem = g_slist_next(elem)) prev = elem; assert(elem); // Mirror should always exists in the list of mirrors next = g_slist_next(elem); if (!success && !next) goto exit; // Penalization not needed - Mirror is already the last one if (success && !prev) goto exit; // Bonus not needed - Mirror is already the first one // Serious errors if (serious && mirror->successful_transfers == 0) { // Mirror that encounter a serious error and has no successful // transfers should be moved at the end of the list // (such mirror is probably down/broken/buggy) GSList *last = g_slist_last(elem); elem->data = last->data; last->data = (gpointer) mirror; g_debug("%s: Mirror %s was moved at the end", __func__, mirror->mirror->url); goto exit; // No more hadling needed } // Calculate ranks rank_cur = mirror_rank(mirror); if (rank_cur < 0.0) goto exit; // Too early to judge if (!success) { // Penalize gdouble rank_next = mirror_rank(next->data); if (rank_next < 0.0 || rank_next > rank_cur) { elem->data = next->data; next->data = (gpointer) mirror; g_debug("%s: Mirror %s was penalized", __func__, mirror->mirror->url); } } else { // Bonus gdouble rank_prev = mirror_rank(prev->data); if (rank_prev < rank_cur) { elem->data = prev->data; prev->data = mirror; g_debug("%s: Mirror %s was awarded", __func__, mirror->mirror->url); } } exit: if (g_getenv("LIBREPO_DEBUG_ADAPTIVEMIRRORSORTING")) { // Debug g_debug("%s: Updated order of mirrors (for %p):", __func__, mirrors); for (GSList *elem = mirrors; elem; elem = g_slist_next(elem)) { LrMirror *m = elem->data; g_debug(" %s (s: %d f: %d)", m->mirror->url, m->successful_transfers, m->failed_transfers); } } return TRUE; } static gboolean check_transfer_statuses(LrDownload *dd, GError **err) { assert(dd); assert(!err || *err == NULL); int msgs_in_queue; CURLMsg *msg; while ((msg = curl_multi_info_read(dd->multi_handle, &msgs_in_queue))) { LrTarget *target = NULL; _cleanup_free_ char *effective_url = NULL; int fd; gboolean matches = TRUE; GError *transfer_err = NULL; GError *tmp_err = NULL; gboolean ret; gboolean serious_error = FALSE; gboolean fatal_error = FALSE; GError *fail_fast_error = NULL; if (msg->msg != CURLMSG_DONE) { // We are only interested in messages about finished transfers continue; } // Find the target with this curl easy handle for (GSList *elem = dd->running_transfers; elem; elem = g_slist_next(elem)) { LrTarget *ltarget = elem->data; if (ltarget->curl_handle == msg->easy_handle) target = ltarget; } assert(target); // Each easy handle used in the multi handle // should always belong to some target from // the running_transfers list curl_easy_getinfo(msg->easy_handle, CURLINFO_EFFECTIVE_URL, &effective_url); effective_url = g_strdup(effective_url); // Make the effective url // persistent to survive // the curl_easy_cleanup() g_debug("Transfer finished: %s (Effective url: %s)", target->target->path, effective_url); // // Check status of finished transfer // ret = check_finished_transfer_status(msg, target, &serious_error, &fatal_error, &transfer_err, err); if (!ret) // Error return FALSE; if (transfer_err) // Transfer was unsuccessful goto transfer_error; // // Checksum checking // fflush(target->f); fd = fileno(target->f); #ifdef WITH_ZCHUNK if (target->target->is_zchunk) { zckCtx *zck = NULL; if (target->zck_state == LR_ZCK_DL_HEADER) { if(target->mirror->max_ranges > 0 && !lr_zck_valid_header(target->target, target->target->path, fd, &transfer_err)) goto transfer_error; } else if(target->zck_state == LR_ZCK_DL_BODY) { if(target->mirror->max_ranges > 0) { zckCtx *zck = zck_dl_get_zck(target->target->zck_dl); if(zck == NULL) { g_set_error(&transfer_err, LR_DOWNLOADER_ERROR, LRE_ZCK, "Unable to get zchunk file from download context"); goto transfer_error; } if(zck_failed_chunks(zck) == 0 && zck_missing_chunks(zck) == 0) target->zck_state = LR_ZCK_DL_FINISHED; } else { if(target->range_fail) { target->range_fail = FALSE; } else { target->zck_state = LR_ZCK_DL_FINISHED; } } } if(target->zck_state == LR_ZCK_DL_FINISHED) { zck = lr_zck_init_read(target->target, target->target->path, fd, &transfer_err); if(!zck) goto transfer_error; if(zck_validate_checksums(zck) < 1) { zck_free(&zck); g_set_error(&transfer_err, LR_DOWNLOADER_ERROR, LRE_BADCHECKSUM, "At least one of the zchunk checksums doesn't match in %s", effective_url); goto transfer_error; } zck_free(&zck); } } else { #endif /* WITH_ZCHUNK */ ret = check_finished_transfer_checksum(fd, target->target->checksums, &matches, &transfer_err, &tmp_err); if (!ret) { // Error g_propagate_prefixed_error(err, tmp_err, "Downloading from %s" "was successful but error encountered while " "checksumming: ", effective_url); return FALSE; } #ifdef WITH_ZCHUNK } #endif /* WITH_ZCHUNK */ if (transfer_err) // Checksum doesn't match goto transfer_error; // // Any other checks should go here // // Preserve timestamp of downloaded file if requested if (target->target->handle && target->target->handle->preservetime) { long remote_filetime = -1; curl_easy_getinfo(target->curl_handle, CURLINFO_FILETIME, &remote_filetime); if (remote_filetime >= 0) { const struct timeval tv[] = {{remote_filetime, 0}, {remote_filetime, 0}}; if (futimes(fileno(target->f), tv) == -1) g_debug("%s: Failed to change timestamps of downloaded file.", __func__); } else { g_debug("%s: Unable to get remote time of retrieved document \"%s\"", __func__, target->target->path); } } transfer_error: // // Cleanup // curl_multi_remove_handle(dd->multi_handle, target->curl_handle); curl_easy_cleanup(target->curl_handle); target->curl_handle = NULL; g_free(target->headercb_interrupt_reason); target->headercb_interrupt_reason = NULL; fclose(target->f); target->f = NULL; if (target->curl_rqheaders) { curl_slist_free_all(target->curl_rqheaders); target->curl_rqheaders = NULL; } dd->running_transfers = g_slist_remove(dd->running_transfers, (gconstpointer) target); target->tried_mirrors = g_slist_append(target->tried_mirrors, target->mirror); if (target->mirror) { gboolean success = transfer_err == NULL; mirror_update_statistics(target->mirror, success); if (dd->adaptivemirrorsorting) sort_mirrors(target->lrmirrors, target->mirror, success, serious_error); } if (transfer_err) { // There was an error during transfer int complete_url_in_path = strstr(target->target->path, "://") ? 1 : 0; guint num_of_tried_mirrors = g_slist_length(target->tried_mirrors); gboolean retry = FALSE; g_info("Error during transfer: %s", transfer_err->message); // Call mirrorfailure callback LrMirrorFailureCb mf_cb = target->target->mirrorfailurecb; if (mf_cb) { int rc = mf_cb(target->target->cbdata, transfer_err->message, effective_url); if (rc == LR_CB_ABORT) { // User wants to abort this download, so make the error fatal fatal_error = TRUE; } else if (rc == LR_CB_ERROR) { gchar *original_err_msg = g_strdup(transfer_err->message); g_clear_error(&transfer_err); g_info("Downloading was aborted by LR_CB_ERROR from " "mirror failure callback. Original error was: %s", original_err_msg); g_set_error(&transfer_err, LR_DOWNLOADER_ERROR, LRE_CBINTERRUPTED, "Downloading was aborted by LR_CB_ERROR from " "mirror failure callback. Original error was: " "%s", original_err_msg); g_free(original_err_msg); fatal_error = TRUE; target->cb_return_code = LR_CB_ERROR; } } if (!fatal_error) { // Temporary error (serious_error) during download occurred and // another transfers are running or there are successful transfers // and fewer failed transfers than tried parallel connections. It may be mirror is OK // but accepts fewer parallel connections. if (serious_error && target->mirror && (has_running_transfers(target->mirror) || (target->mirror->successful_transfers > 0 && target->mirror->failed_transfers < target->mirror->max_tried_parallel_connections))) { g_debug("%s: Lower maximum of allowed parallel connections for this mirror", __func__); if (has_running_transfers(target->mirror)) target->mirror->allowed_parallel_connections = target->mirror->running_transfers; else target->mirror->allowed_parallel_connections = 1; // Give used mirror another chance target->tried_mirrors = g_slist_remove(target->tried_mirrors, target->mirror); num_of_tried_mirrors = g_slist_length(target->tried_mirrors); } // complete_url_in_path and target->baseurl doesn't have an alternatives like using // mirrors, therefore they are handled differently const char * complete_url_or_baseurl = complete_url_in_path ? target->target->path : target->target->baseurl; if (can_retry_download(dd, num_of_tried_mirrors, complete_url_or_baseurl)) { // Try another mirror or retry if (complete_url_or_baseurl) { g_debug("%s: Ignore error - Retry download", __func__); } else { g_debug("%s: Ignore error - Try another mirror", __func__); } target->state = LR_DS_WAITING; retry = TRUE; g_error_free(transfer_err); // Ignore the error // Truncate file - remove downloaded garbage (error html page etc.) #ifdef WITH_ZCHUNK if (!target->target->is_zchunk || target->zck_state == LR_ZCK_DL_HEADER) { #endif if (!truncate_transfer_file(target, err)) return FALSE; #ifdef WITH_ZCHUNK } #endif } } if (!retry) { // No more mirrors to try or baseurl used or fatal error g_debug("%s: No more retries (tried: %d)", __func__, num_of_tried_mirrors); target->state = LR_DS_FAILED; // Call end callback LrEndCb end_cb = target->target->endcb; if (end_cb) { int rc = end_cb(target->target->cbdata, LR_TRANSFER_ERROR, transfer_err->message); if (rc == LR_CB_ERROR) { target->cb_return_code = LR_CB_ERROR; g_debug("%s: Downloading was aborted by LR_CB_ERROR " "from end callback", __func__); } } lr_downloadtarget_set_error(target->target, transfer_err->code, "Download failed: %s", transfer_err->message); if (dd->failfast) { // Fail fast is enabled, fail on any error g_propagate_error(&fail_fast_error, transfer_err); } else if (target->cb_return_code == LR_CB_ERROR) { // Callback returned LR_CB_ERROR, abort the downloading g_debug("%s: Downloading was aborted by LR_CB_ERROR", __func__); g_propagate_error(&fail_fast_error, transfer_err); } else { // Fail fast is disabled and callback doesn't repor serious // error, so this download is aborted, but other download // can continue (do not abort whole downloading) g_error_free(transfer_err); } } } else { #ifdef WITH_ZCHUNK // No error encountered, transfer finished successfully if(target->target->is_zchunk && target->zck_state != LR_ZCK_DL_FINISHED) { // If we haven't finished downloading zchunk file, setup next // download target->state = LR_DS_WAITING; target->original_offset = -1; target->target->rcode = LRE_UNFINISHED; target->target->err = "Not finished"; target->handle = target->target->handle; target->tried_mirrors = g_slist_remove(target->tried_mirrors, target->mirror); } else { #endif /* WITH_ZCHUNK */ target->state = LR_DS_FINISHED; // Remove xattr that states that the file is being downloaded // by librepo, because the file is now completely downloaded // and the xattr is not needed (is is useful only for resuming) remove_librepo_xattr(target->target); // Call end callback LrEndCb end_cb = target->target->endcb; if (end_cb) { int rc = end_cb(target->target->cbdata, LR_TRANSFER_SUCCESSFUL, NULL); if (rc == LR_CB_ERROR) { target->cb_return_code = LR_CB_ERROR; g_debug("%s: Downloading was aborted by LR_CB_ERROR " "from end callback", __func__); g_set_error(&fail_fast_error, LR_DOWNLOADER_ERROR, LRE_CBINTERRUPTED, "Interrupted by LR_CB_ERROR from end callback"); } } if (target->mirror) lr_downloadtarget_set_usedmirror(target->target, target->mirror->mirror->url); #ifdef WITH_ZCHUNK } #endif /* WITH_ZCHUNK */ lr_downloadtarget_set_error(target->target, LRE_OK, NULL); lr_downloadtarget_set_effectiveurl(target->target, effective_url); } if (fail_fast_error) { // Interrupt whole downloading // A fatal error occurred or interrupted by callback g_propagate_error(err, fail_fast_error); return FALSE; } } // At this point, after handles of finished transfers were removed // from the multi_handle, we could add new waiting transfers. return prepare_next_transfers(dd, err); } static gboolean lr_perform(LrDownload *dd, GError **err) { CURLMcode cm_rc; // CurlM_ReturnCode assert(dd); assert(!err || *err == NULL); while (1) { int still_running = 0; cm_rc = curl_multi_perform(dd->multi_handle, &still_running); if (lr_interrupt) { // Check interrupt after each call of curl_multi_perform g_set_error(err, LR_DOWNLOADER_ERROR, LRE_INTERRUPTED, "Interrupted by signal"); return FALSE; } if (cm_rc != CURLM_OK) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_CURLM, "curl_multi_perform() error: %s", curl_multi_strerror(cm_rc)); return FALSE; } // Check if any handle finished and potentially add one or more // waiting downloads to the multi_handle. int rc = check_transfer_statuses(dd, err); if (!rc) return FALSE; // Leave if there's nothing to wait for if (!still_running && !dd->running_transfers) break; long curl_timeout = -1; cm_rc = curl_multi_timeout(dd->multi_handle, &curl_timeout); if (cm_rc != CURLM_OK) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_CURLM, "curl_multi_timeout() error: %s", curl_multi_strerror(cm_rc)); return FALSE; } if (curl_timeout <= 0) // No wait continue; if (curl_timeout > 1000) // Wait no more than 1s curl_timeout = 1000; cm_rc = curl_multi_wait(dd->multi_handle, NULL, 0, curl_timeout, NULL); if (cm_rc != CURLM_OK) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_CURLM, "curl_multi_wait() error: %s", curl_multi_strerror(cm_rc)); return FALSE; } } return TRUE; } gboolean lr_download(GSList *targets, gboolean failfast, GError **err) { gboolean ret = FALSE; LrDownload dd; // dd stands for Download Data GError *tmp_err = NULL; assert(!err || *err == NULL); if (lr_interrupt) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_INTERRUPTED, "Interrupted by signal"); return FALSE; } if (!targets) { g_debug("%s: No targets", __func__); return TRUE; } // XXX: Downloader configuration (max parallel connections etc.) // is taken from the handle of the first target. LrHandle *lr_handle = ((LrDownloadTarget *) targets->data)->handle; // Prepare download data dd.failfast = failfast; if (lr_handle) { dd.max_parallel_connections = lr_handle->maxparalleldownloads; dd.max_connection_per_host = lr_handle->maxdownloadspermirror; dd.max_mirrors_to_try = lr_handle->maxmirrortries; dd.allowed_mirror_failures = lr_handle->allowed_mirror_failures; dd.adaptivemirrorsorting = lr_handle->adaptivemirrorsorting; } else { // No handle, this is allowed when a complete URL is passed // via relative_url param. dd.max_parallel_connections = LRO_MAXPARALLELDOWNLOADS_DEFAULT; dd.max_connection_per_host = LRO_MAXDOWNLOADSPERMIRROR_DEFAULT; dd.max_mirrors_to_try = LRO_MAXMIRRORTRIES_DEFAULT; dd.allowed_mirror_failures = LRO_ALLOWEDMIRRORFAILURES_DEFAULT; dd.adaptivemirrorsorting = LRO_ADAPTIVEMIRRORSORTING_DEFAULT; } dd.multi_handle = curl_multi_init(); if (!dd.multi_handle) { // Something went wrong g_set_error(err, LR_DOWNLOADER_ERROR, LRE_CURLM, "curl_multi_init() call failed"); return FALSE; } // Prepare list of LrTargets and LrHandleMirrors dd.handle_mirrors = NULL; dd.targets = NULL; for (GSList *elem = targets; elem; elem = g_slist_next(elem)) { LrDownloadTarget *dtarget = elem->data; // Assertions assert(dtarget); assert(dtarget->path); assert((dtarget->fd > 0 && !dtarget->fn) || (dtarget->fd < 0 && dtarget->fn)); g_debug("%s: Target: %s (%s)", __func__, dtarget->path, (dtarget->baseurl) ? dtarget->baseurl : "-"); // Cleanup of LrDownloadTarget lr_downloadtarget_reset(dtarget); // Create and fill LrTarget LrTarget *target = lr_malloc0(sizeof(*target)); target->state = LR_DS_WAITING; target->target = dtarget; target->original_offset = -1; target->resume = dtarget->resume; target->target->rcode = LRE_UNFINISHED; target->target->err = "Not finished"; target->handle = dtarget->handle; dd.targets = g_slist_append(dd.targets, target); // Add list of handle internal mirrors to dd.handle_mirrors // if doesn't exists yet and set the list reference // to the target. dd.handle_mirrors = lr_prepare_lrmirrors(dd.handle_mirrors, target); } dd.running_transfers = NULL; // Prepare the first set of transfers if (!prepare_next_transfers(&dd, &tmp_err)) goto lr_download_cleanup; // Perform! g_debug("%s: Downloading started", __func__); ret = lr_perform(&dd, &tmp_err); assert(ret || tmp_err); lr_download_cleanup: if (tmp_err) { // If there was an error, stop all transfers that are in progress. g_info("Error while downloading: %s", tmp_err->message); for (GSList *elem = dd.running_transfers; elem; elem = g_slist_next(elem)){ LrTarget *target = elem->data; curl_multi_remove_handle(dd.multi_handle, target->curl_handle); curl_easy_cleanup(target->curl_handle); target->curl_handle = NULL; fclose(target->f); target->f = NULL; g_free(target->headercb_interrupt_reason); target->headercb_interrupt_reason = NULL; // Call end callback LrEndCb end_cb = target->target->endcb; if (end_cb) { gchar *msg = g_strdup_printf("Not finished - interrupted by " "error: %s", tmp_err->message); end_cb(target->target->cbdata, LR_TRANSFER_ERROR, msg); // No need to check end_cb return value, because there // already was an error g_free(msg); } lr_downloadtarget_set_error(target->target, LRE_UNFINISHED, "Not finished - interrupted by error: %s", tmp_err->message); } g_slist_free(dd.running_transfers); dd.running_transfers = NULL; g_propagate_error(err, tmp_err); } assert(dd.running_transfers == NULL); curl_multi_cleanup(dd.multi_handle); // Clean up dd.handle_mirrors for (GSList *elem = dd.handle_mirrors; elem; elem = g_slist_next(elem)) { LrHandleMirrors *handle_mirrors = elem->data; for (GSList *el = handle_mirrors->lrmirrors; el; el = g_slist_next(el)) { LrMirror *mirror = el->data; lr_free(mirror); } g_slist_free(handle_mirrors->lrmirrors); lr_free(handle_mirrors); } g_slist_free(dd.handle_mirrors); // Clean up targets for (GSList *elem = dd.targets; elem; elem = g_slist_next(elem)) { LrTarget *target = elem->data; assert(target->curl_handle == NULL); assert(target->f == NULL); // Remove file created for the target if download was // unsuccessful and the file doesn't exists before or // its original content was overwritten if (target->state != LR_DS_FINISHED) { if (!target->resume || target->original_offset == 0) { // Remove target file if the file doesn't // exist before or was empty or was overwritten if (target->target->fn) { // We can remove only files that were specified by fn if (unlink(target->target->fn) != 0) { g_warning("Error while removing: %s", g_strerror(errno)); } } } } g_slist_free(target->tried_mirrors); lr_free(target); } g_slist_free(dd.targets); return ret; } gboolean lr_download_target(LrDownloadTarget *target, GError **err) { gboolean ret; GSList *list = NULL; assert(!err || *err == NULL); if (!target) return TRUE; list = g_slist_prepend(list, target); ret = lr_download(list, TRUE, err); g_slist_free(list); return ret; } gboolean lr_download_url(LrHandle *lr_handle, const char *url, int fd, GError **err) { return lr_yum_download_url(lr_handle, url, fd, FALSE, FALSE, err); } int lr_multi_progress_func(void* ptr, double total_to_download, double now_downloaded) { LrCallbackData *cbdata = ptr; LrSharedCallbackData *shared_cbdata = cbdata->sharedcbdata; if (cbdata->downloaded > now_downloaded || cbdata->total != total_to_download) { // Reset counters // This is not first mirror for the transfer, // we have already downloaded some data cbdata->total = total_to_download; // Call progress cb with zeroized params // This should tell progress cb, that the total_to_download // size is changed. int ret = shared_cbdata->cb(cbdata->userdata, 0.0, 0.0); if (ret != LR_CB_OK) return ret; } cbdata->downloaded = now_downloaded; // Prepare values for the user callback double totalsize = 0.0; double downloaded = 0.0; for (GSList *elem = shared_cbdata->singlecbdata; elem; elem = g_slist_next(elem)) { LrCallbackData *singlecbdata = elem->data; totalsize += singlecbdata->total; downloaded += singlecbdata->downloaded; } if (downloaded > totalsize) totalsize = downloaded; // Call user callback return shared_cbdata->cb(cbdata->userdata, totalsize, downloaded); } int lr_multi_mf_func(void *ptr, const char *msg, const char *url) { LrCallbackData *cbdata = ptr; LrSharedCallbackData *shared_cbdata = cbdata->sharedcbdata; return shared_cbdata->mfcb(cbdata->userdata, msg, url); } gboolean lr_download_single_cb(GSList *targets, gboolean failfast, LrProgressCb cb, LrMirrorFailureCb mfcb, GError **err) { gboolean ret; LrSharedCallbackData shared_cbdata; assert(!err || *err == NULL); shared_cbdata.cb = cb; shared_cbdata.mfcb = mfcb; shared_cbdata.singlecbdata = NULL; // "Inject" callbacks and callback data to the targets for (GSList *elem = targets; elem; elem = g_slist_next(elem)) { LrDownloadTarget *target = elem->data; LrCallbackData *lrcbdata = lr_malloc0(sizeof(*lrcbdata)); lrcbdata->downloaded = 0.0; lrcbdata->total = 0.0; lrcbdata->userdata = target->cbdata; lrcbdata->sharedcbdata = &shared_cbdata; target->progresscb = (cb) ? lr_multi_progress_func : NULL; target->mirrorfailurecb = (mfcb) ? lr_multi_mf_func : NULL; target->cbdata = lrcbdata; shared_cbdata.singlecbdata = g_slist_append(shared_cbdata.singlecbdata, lrcbdata); } ret = lr_download(targets, failfast, err); // Remove callbacks and callback data for (GSList *elem = targets; elem; elem = g_slist_next(elem)) { LrDownloadTarget *target = elem->data; LrCallbackData *cbdata = target->cbdata; target->cbdata = cbdata->userdata; target->progresscb = NULL; target->mirrorfailurecb = NULL; lr_free(cbdata); } g_slist_free(shared_cbdata.singlecbdata); return ret; } librepo-1.12.1/librepo/downloader.h000066400000000000000000000075001372027637700172130ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_DOWNLOADER_H__ #define __LR_DOWNLOADER_H__ #include #include "handle.h" #include "downloadtarget.h" G_BEGIN_DECLS /** \defgroup downloader Downloading API * \addtogroup downloader * @{ */ #define LR_DOWNLOADER_MAXIMAL_RESUME_COUNT 1 /** Global variable signalizing if SIGINT was catched. */ extern volatile sig_atomic_t lr_interrupt; /** SIGINT Signal handler. * @param sig Signal number. */ void lr_sigint_handler(int sig); /** Main download function. * @param targets GSList with one or more ::LrDownloadTarget. * Could be NULL. Then return immediately with LRE_OK * return code. * @param failfast If True, fail imediatelly after first download failed * (after max allowed number of retries or all available * mirrors are tried without success). * @param err GError ** * @return If FALSE then err is set. * Note: If failfast is FALSE, then return value TRUE * doesn't have to mean that all targets was * downloaded successfully - You have to check * status of all downloaded targets. */ gboolean lr_download(GSList *targets, gboolean failfast, GError **err); /** Wrapper over ::lr_download that takes only single ::LrDownloadTarget. * Note: failfast is TRUE, so if download failed, then this function returns * FALSE (There is no need to check status of download itself). * @param target single LrDownloadTarget object or NULL. * @param err GError ** * @return See ::lr_download */ gboolean lr_download_target(LrDownloadTarget *target, GError **err); /** Wrapper over the ::lr_download_target that takes only url and fd. * After download fd is sought to the begin of the file. * Note: failfast is TRUE, so if download failed, then this function returns * FALSE (There is no need to check status of download itself). * @param handle See ::lr_download * @param url URL (absolute or relative) * @param fd Opened file descriptor. * @param err GError ** * @return See ::lr_download */ gboolean lr_download_url(LrHandle *handle, const char *url, int fd, GError **err); /** Wrapper over the ::lr_download that calculate collective statistics of * all downloads and repord them via callback. Note: All callbacks and * userdata setted in targets will be replaced and don't be used. * @param targets See ::lr_download * @param failfast See ::lr_download * @param cb Progress callback ::LrProgressCb * @param mfcb Mirror Failure Callback ::LrMirrorFailureCb * @param err GError ** * @return See ::lr_download */ gboolean lr_download_single_cb(GSList *targets, gboolean failfast, LrProgressCb cb, LrMirrorFailureCb mfcb, GError **err); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/downloader_internal.h000066400000000000000000000030631372027637700211070ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2016 Martin Hatina * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef LIBREPO_DOWNLOADER_INTERNAL_H #define LIBREPO_DOWNLOADER_INTERNAL_H #include "handle.h" typedef struct { LrProgressCb cb; /*!< User callback */ LrMirrorFailureCb mfcb; /*!< Mirror failure callback */ GSList *singlecbdata; /*!< List of LrCallbackData */ } LrSharedCallbackData; typedef struct { double downloaded; /*!< Currently downloaded bytes of target */ double total; /*!< Total size of the target */ void *userdata; /*!< User data related to the target */ LrSharedCallbackData *sharedcbdata; /*!< Shared cb data */ } LrCallbackData; #endif //LIBREPO_DOWNLOADER_INTERNAL_H librepo-1.12.1/librepo/downloadtarget.c000066400000000000000000000127131372027637700200700ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "util.h" #include "downloadtarget.h" #include "downloadtarget_internal.h" #include "cleanup.h" #include "handle_internal.h" LrDownloadTargetChecksum * lr_downloadtargetchecksum_new(LrChecksumType type, const gchar *value) { LrDownloadTargetChecksum *dtch = lr_malloc0(sizeof(*dtch)); dtch->type = type; dtch->value = g_strdup(value); return dtch; } void lr_downloadtargetchecksum_free(LrDownloadTargetChecksum *dtch) { if (!dtch) return; g_free(dtch->value); g_free(dtch); } LrDownloadTarget * lr_downloadtarget_new(LrHandle *handle, const char *path, const char *baseurl, int fd, const char *fn, GSList *possiblechecksums, gint64 expectedsize, gboolean resume, LrProgressCb progresscb, void *cbdata, LrEndCb endcb, LrMirrorFailureCb mirrorfailurecb, void *userdata, gint64 byterangestart, gint64 byterangeend, char *range, gboolean no_cache, gboolean is_zchunk) { LrDownloadTarget *target; _cleanup_free_ gchar *final_path = NULL; _cleanup_free_ gchar *final_baseurl = NULL; assert(path); assert((fd >= 0 && !fn) || (fd < 0 && fn)); if (byterangestart && resume) { g_warning("Cannot specify byterangestart and set resume to TRUE at the same time"); return NULL; } // Substitute variables in URLs if (handle && handle->urlvars) { final_path = lr_url_substitute(path, handle->urlvars); final_baseurl = lr_url_substitute(baseurl, handle->urlvars); } else { final_path = g_strdup(path); final_baseurl = g_strdup(baseurl); } target = lr_malloc0(sizeof(*target)); target->handle = handle; target->chunk = g_string_chunk_new(0); target->path = g_string_chunk_insert(target->chunk, final_path); target->baseurl = lr_string_chunk_insert(target->chunk, final_baseurl); target->fd = fd; target->fn = lr_string_chunk_insert(target->chunk, fn); target->checksums = possiblechecksums; target->expectedsize = expectedsize; target->origsize = expectedsize; target->resume = resume; target->progresscb = progresscb; target->cbdata = cbdata; target->endcb = endcb; target->mirrorfailurecb = mirrorfailurecb; target->rcode = LRE_UNFINISHED; target->userdata = userdata; target->byterangestart = byterangestart; target->byterangeend = byterangeend; target->range = range; target->no_cache = no_cache; target->is_zchunk = is_zchunk; return target; } void lr_downloadtarget_reset(LrDownloadTarget *target) { if (!target) return; target->usedmirror = NULL; target->effectiveurl = NULL; target->rcode = LRE_OK; target->err = NULL; } void lr_downloadtarget_free(LrDownloadTarget *target) { if (!target) return; g_slist_free_full(target->checksums, (GDestroyNotify) lr_downloadtargetchecksum_free); g_string_chunk_free(target->chunk); lr_free(target); } void lr_downloadtarget_set_error(LrDownloadTarget *target, LrRc code, const char *format, ...) { assert(target); assert(code == LRE_OK || format); if (format) { int ret; va_list vl; gchar *message = NULL; va_start(vl, format); ret = g_vasprintf(&message, format, vl); va_end(vl); if (ret < 0) { assert(0); target->err = ""; return; } target->err = lr_string_chunk_insert(target->chunk, message); g_free(message); } else { target->err = NULL; } target->rcode = code; } void lr_downloadtarget_set_usedmirror(LrDownloadTarget *target, const char *url) { assert(target); target->usedmirror = lr_string_chunk_insert(target->chunk, url); } void lr_downloadtarget_set_effectiveurl(LrDownloadTarget *target, const char *url) { assert(target); target->effectiveurl = lr_string_chunk_insert(target->chunk, url); } librepo-1.12.1/librepo/downloadtarget.h000066400000000000000000000222151372027637700200730ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_DOWNLOADTARGET_H__ #define __LR_DOWNLOADTARGET_H__ #include #ifdef WITH_ZCHUNK #include #endif /* WITH_ZCHUNK */ #include "handle.h" #include "rcodes.h" #include "checksum.h" #include "types.h" #include "yum.h" G_BEGIN_DECLS typedef struct { LrChecksumType type; gchar *value; } LrDownloadTargetChecksum; /** Create new LrDownloadTargetChecksum object. * @param type Checksum type * @param value Checksum value. This value will be stduped. */ LrDownloadTargetChecksum * lr_downloadtargetchecksum_new(LrChecksumType type, const gchar *value); /** Free LrDownloadTargetChecksum object. * @param dtch LrDownloadTargetChecksum object */ void lr_downloadtargetchecksum_free(LrDownloadTargetChecksum *dtch); /** Single download target */ typedef struct { LrHandle *handle; /*!< Handle */ char *path; /*!< Relative path for URL (URL: "http://foo.bar/stuff", path: "somestuff.xml") */ char *baseurl; /*!< Base URL for this target. If used, then mirrorlist will be ignored. */ int fd; /*!< Opened file descriptor where data will be written or -1. Note: Only one, fd or fn, is set simultaneously. */ char *fn; /*!< Filename where data will be written or NULL. Note: Only one, fd or fn, is set simultaneously. */ GSList *checksums; /*!< NULL or GSList with pointers to LrDownloadTargetChecksum structures. With possible checksums of the file. Checksum check is stopped right after first match. Useful in situation when the file could has one from set of available checksums. E.g. element in metalink could contain alternate checksums for the repomd.xml, because metalink and mirrors could be out of sync for a while. */ gint64 expectedsize; /*!< Expected size of the target */ gint64 origsize; /*!< Original expected size of the target. Sometimes expectedsize will change, especially if zchunk is in use, but this will never change */ gboolean resume; /*!< Resume: 0 - no resume, download whole file, otherwise - autodetect offset for resume */ LrProgressCb progresscb; /*!< Progression callback for the target */ void *cbdata; /*!< Callback user data */ LrEndCb endcb; /*!< Callback called when target transfer is done. (Use status to check if successfully or unsuccessfully) */ LrMirrorFailureCb mirrorfailurecb; /*!< Called when download from a mirror failed. */ GStringChunk *chunk; /*!< Chunk for strings used in this structure. */ gint64 byterangestart; /*!< Download only specified range of bytes. */ gint64 byterangeend; /*!< Download only specified range of bytes. */ gboolean no_cache; /*!< Add headers that tell proxy server to provide a fresh data instead of cached one. */ // Items filled by downloader char *usedmirror; /*!< Used mirror. Filled only if transfer was successful. */ char *effectiveurl; /*!< Effective url. Filled only if transfer was successful. */ LrRc rcode; /*!< Return code */ char *err; /*!< NULL or error message */ // Other items void *userdata; /*!< User data - This data are not used by lr_downloader or touched by lr_downloadtarget_free. */ // New options for zchunk - put at end to maintain API stability gboolean is_zchunk; /*!< Whether record is a zchunk file or not */ char *range; /*!< Range string to download, overrides byterangestart and end */ #ifdef WITH_ZCHUNK zckDL *zck_dl; /*!< Zchunk download context */ gint64 zck_header_size; /*!< Zchunk header size */ double total_to_download; /*!< Total to download in zchunk file */ double downloaded; /*!< Amount already downloaded in zchunk file */ #endif /* WITH_ZCHUNK */ } LrDownloadTarget; /** Create new empty ::LrDownloadTarget. * @param handle Handle or NULL * @param path Absolute or relative URL path * @param baseurl Base URL for relative path specified in path param * @param fd Opened file descriptor where data will be written * or -1. * Note: Set this or fn, no both! * @param fn Filename where data will be written or NULL. * Note: Set this or fd, no both! * @param possiblechecksums NULL or GSList with pointers to * LrDownloadTargetChecksum structures. With possible * checksums of the file. Checksum check is stopped * right after first match. Useful in situation when * the file could has one from set of available * checksums. * E.g. element in metalink could * contain alternate checksums for the repomd.xml, * because metalink and mirrors could be out of * sync for a while. * Note: LrDownloadTarget takes responsibility * for destroying this list! So the list must not * be destroyed by the user, since it is passed * to this function. * @param expectedsize Expected size of the target. If mirror reports * different size, then no download is performed. * If 0 then size of downloaded target is not checked. * @param resume If FALSE, then no resume is done and whole file is * downloaded again. Otherwise offset will be * automatically detected from opened file descriptor * by seek to the end. * @param progresscb Progression callback or NULL * @param cbdata Callback data or NULL * @param endcb Callback called when target transfer is done. * (Use status to check if successfully * or unsuccessfully) * @param mirrorfailurecb Called when download from a mirror failed. * @param userdata This variable could be used to store some user * data. This data are not used/touched by * lr_download* functions or by * lr_downloadtarget_free(). * @param byterangestart Download only specified byte range. 0 is default. * @param byterangeend Download only specified byte range. * If this value is less or equal byterangestart, then * it is ignored. 0 is default. * @param no_cache Tell proxy server that we don't want to use cache * for this request and we want fresh data. * @param is_zchunk This target is a zchunk file, so be ready to use * different download code paths * @return New allocated target */ LrDownloadTarget * lr_downloadtarget_new(LrHandle *handle, const char *path, const char *baseurl, int fd, const char *fn, GSList *possiblechecksums, gint64 expectedsize, gboolean resume, LrProgressCb progresscb, void *cbdata, LrEndCb endcb, LrMirrorFailureCb mirrorfailurecb, void *userdata, gint64 byterangestart, gint64 byterangeend, char *range, gboolean no_cache, gboolean is_zchunk); /** Reset download data filled during downloading. E.g. Error messages, * effective URL, used mirror etc. * @param target Target */ void lr_downloadtarget_reset(LrDownloadTarget *target); /** Free a ::LrDownloadTarget element and its content. * @param target Target to free. */ void lr_downloadtarget_free(LrDownloadTarget *target); G_END_DECLS #endif librepo-1.12.1/librepo/downloadtarget_internal.h000066400000000000000000000033121372027637700217640ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_DOWNLOADTARGET_INTERNAL_H__ #define __LR_DOWNLOADTARGET_INTERNAL_H__ #include #include "downloadtarget.h" #include "types.h" G_BEGIN_DECLS /** Helper function to comfortable setting of error to the ::LrDownloadTarget. */ void lr_downloadtarget_set_error(LrDownloadTarget *target, LrRc code, const char *format, ...); /** Helper function to comfortable setting usedmirror attribute * of ::LrDownloadTarget. */ void lr_downloadtarget_set_usedmirror(LrDownloadTarget *target, const char *url); /** Helper function to comfortable setting effectiveurl attribute * of ::ld_DownloadTarget */ void lr_downloadtarget_set_effectiveurl(LrDownloadTarget *target, const char *url); G_END_DECLS #endif librepo-1.12.1/librepo/fastestmirror.c000066400000000000000000000647771372027637700177770ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "util.h" #include "cleanup.h" #include "handle_internal.h" #include "rcodes.h" #include "fastestmirror.h" #include "fastestmirror_internal.h" #define LENGTH_OF_MEASUREMENT 2.0 // Number of seconds (float point!) #define HALF_OF_SECOND_IN_MICROS 500000 #define CACHE_GROUP_METADATA ":_librepo_:" // Group with metadata #define CACHE_KEY_TS "ts" // Timestamp // FIXME: next time the cache version is updated, please fix this typo! It // should obviously have been "connecttime". #define CACHE_KEY_CONNECTTIME "connectime" // Time of response #define CACHE_KEY_VERSION "version" // Version of cache format #define CACHE_VERSION 1 // Current version of cache format #define CACHE_RECORD_MAX_AGE (LRO_FASTESTMIRRORMAXAGE_DEFAULT * 6) typedef struct { gchar *path; GKeyFile *keyfile; } LrFastestMirrorCache; static LrFastestMirror * lr_lrfastestmirror_new() { LrFastestMirror *mirror = g_new0(LrFastestMirror, 1); mirror->plain_connect_time = 0.0; mirror->cached = FALSE; return mirror; } void lr_lrfastestmirror_free(LrFastestMirror *mirror) { if (!mirror) return; if (mirror->curl) curl_easy_cleanup(mirror->curl); g_free(mirror); } static gboolean lr_fastestmirrorcache_load(LrFastestMirrorCache **cache, gchar *path, LrFastestMirrorCb cb, void *cbdata, GError **err) { assert(cache); assert(!err || *err == NULL); if (!path) { // No cache file specified *cache = NULL; return TRUE; } cb(cbdata, LR_FMSTAGE_CACHELOADING, path); GKeyFile *keyfile = g_key_file_new(); *cache = lr_malloc0(sizeof(LrFastestMirrorCache)); (*cache)->path = g_strdup(path); (*cache)->keyfile = keyfile; if (!g_file_test(path, G_FILE_TEST_EXISTS)) { // Cache file doesn't exist cb(cbdata, LR_FMSTAGE_CACHELOADINGSTATUS, "Cache doesn't exist"); } else { // Cache exists, try to load it gboolean something_wrong = FALSE; GError *tmp_err = NULL; gboolean ret = g_key_file_load_from_file(keyfile, path, G_KEY_FILE_NONE, &tmp_err); if (!ret) { // Cannot parse cache file char *msg = g_strdup_printf("Cannot parse fastestmirror " "cache %s: %s", path, tmp_err->message); g_debug("%s: %s", __func__, msg); cb(cbdata, LR_FMSTAGE_CACHELOADINGSTATUS, msg); something_wrong = TRUE; g_free(msg); g_error_free(tmp_err); } else { // File parsed successfully if (!g_key_file_has_group(keyfile, CACHE_GROUP_METADATA)) { // Not a fastestmirror cache g_debug("%s: File %s is not a fastestmirror cache file", __func__, path); cb(cbdata, LR_FMSTAGE_CACHELOADINGSTATUS, "File is not a fastestmirror cache"); something_wrong = TRUE; } else { int version = (int) g_key_file_get_integer(keyfile, CACHE_GROUP_METADATA, CACHE_KEY_VERSION, NULL); if (version != CACHE_VERSION) { g_debug("%s: Old cache version %d vs %d", __func__, version, CACHE_VERSION); cb(cbdata, LR_FMSTAGE_CACHELOADINGSTATUS, "Old version of cache format"); something_wrong = TRUE; } } } if (something_wrong) { // Reinit keyfile g_key_file_free(keyfile); keyfile = g_key_file_new(); (*cache)->keyfile = keyfile; } else { gsize len; gchar **array = g_key_file_get_groups(keyfile, &len); g_debug("%s: Loaded: %"G_GSIZE_FORMAT" records", __func__, len); // Remove really outdated records gint64 current_time = g_get_real_time() / 1000000; char **group, *groupname; for (group=array; groupname=*group, groupname; group++) { if (g_str_has_prefix(groupname, ":_")) continue; gint64 ts = g_key_file_get_int64(keyfile, groupname, CACHE_KEY_TS, NULL); if (ts < (current_time - CACHE_RECORD_MAX_AGE)) { // Record is too old, remove it g_debug("%s: Removing too old record from cache: %s " "(ts: %"G_GINT64_FORMAT")", __func__, groupname, ts); g_key_file_remove_group(keyfile, groupname, NULL); } } g_strfreev(array); cb(cbdata, LR_FMSTAGE_CACHELOADINGSTATUS, NULL); } } // Set version of cache format g_key_file_set_integer(keyfile, CACHE_GROUP_METADATA, CACHE_KEY_VERSION, CACHE_VERSION); return TRUE; } static gboolean lr_fastestmirrorcache_lookup(LrFastestMirrorCache *cache, gchar *url, gint64 *ts, double *connecttime) { if (!cache || !cache->keyfile || !url) return FALSE; GKeyFile *keyfile = cache->keyfile; if (!g_key_file_has_group(keyfile, url)) return FALSE; gint64 l_ts; double l_connecttime; GError *tmp_err = NULL; // Get timestamp l_ts = g_key_file_get_int64(keyfile, url, CACHE_KEY_TS, &tmp_err); if (tmp_err) { g_error_free(tmp_err); return FALSE; } // Get connect time l_connecttime = (double) g_key_file_get_double(keyfile, url, CACHE_KEY_CONNECTTIME, &tmp_err); if (tmp_err) { g_error_free(tmp_err); return FALSE; } *ts = l_ts; *connecttime = l_connecttime; return TRUE; } static void lr_fastestmirrorcache_update(LrFastestMirrorCache *cache, gchar *url, gint64 ts, double connecttime) { if (!cache || !cache->keyfile || !url) return; GKeyFile *keyfile = cache->keyfile; g_key_file_set_int64(keyfile, url, CACHE_KEY_TS, ts); g_key_file_set_double(keyfile, url, CACHE_KEY_CONNECTTIME, connecttime); } static gboolean lr_fastestmirrorcache_write(LrFastestMirrorCache *cache, GError **err) { assert(!err || *err == NULL); if (!cache || !cache->keyfile) return TRUE; // Gen cache content GError *tmp_err = NULL; _cleanup_free_ gchar *content = g_key_file_to_data(cache->keyfile, NULL, &tmp_err); if (tmp_err) { g_propagate_error(err, tmp_err); return FALSE; } // Write cache FILE *f = fopen(cache->path, "w"); if (!f) { g_set_error(err, LR_FASTESTMIRROR_ERROR, LRE_IO, "Cannot open %s: %s", cache->path, g_strerror(errno)); return FALSE; } fputs(content, f); fclose(f); return TRUE; } static void lr_fastestmirrorcache_free(LrFastestMirrorCache *cache) { if (!cache) return; g_free(cache->path); g_key_file_free(cache->keyfile); g_free(cache); } /** Create list of LrFastestMirror based on input list of URLs. */ static gboolean lr_fastestmirror_prepare(LrHandle *handle, GSList *in_list, GSList **out_list, LrFastestMirrorCache *cache, GError **err) { gboolean ret = TRUE; GSList *list = NULL; assert(!err || *err == NULL); if (!in_list) { *out_list = NULL; return TRUE; } gint64 maxage = LRO_FASTESTMIRRORMAXAGE_DEFAULT; gint64 current_time = g_get_real_time() / 1000000; if (handle) maxage = (gint64) handle->fastestmirrormaxage; for (GSList *elem = in_list; elem; elem = g_slist_next(elem)) { gchar *url = elem->data; CURLcode curlcode; CURL *curlh; // Try to find item in the cache gint64 ts; double connecttime; if (lr_fastestmirrorcache_lookup(cache, url, &ts, &connecttime)) { if (ts >= (current_time - maxage)) { // Use cached entry g_debug("%s: Using cached connect time for: %s (%f)", __func__, url, connecttime); LrFastestMirror *mirror = lr_lrfastestmirror_new(); mirror->url = url; mirror->curl = NULL; mirror->plain_connect_time = connecttime; mirror->cached = TRUE; list = g_slist_append(list, mirror); continue; } else { g_debug("%s: Cached connect time too old: %s", __func__, url); } } else { g_debug("%s: Not found in cache: %s", __func__, url); } if (handle) curlh = curl_easy_duphandle(handle->curl_handle); else curlh = lr_get_curl_handle(); if (!curlh) { g_set_error(err, LR_FASTESTMIRROR_ERROR, LRE_CURL, "Cannot create curl handle"); ret = FALSE; break; } LrFastestMirror *mirror = lr_lrfastestmirror_new(); mirror->url = url; mirror->curl = curlh; curlcode = curl_easy_setopt(curlh, CURLOPT_URL, url); if (curlcode != CURLE_OK) { g_set_error(err, LR_FASTESTMIRROR_ERROR, LRE_CURL, "curl_easy_setopt(_, CURLOPT_URL, %s) failed: %s", url, curl_easy_strerror(curlcode)); ret = FALSE; break; } curlcode = curl_easy_setopt(curlh, CURLOPT_CONNECT_ONLY, 1); if (curlcode != CURLE_OK) { g_set_error(err, LR_FASTESTMIRROR_ERROR, LRE_CURL, "curl_easy_setopt(_, CURLOPT_CONNECT_ONLY, 1) failed: %s", curl_easy_strerror(curlcode)); ret = FALSE; break; } list = g_slist_append(list, mirror); } if (ret) { *out_list = list; } else { assert(!err || *err); g_slist_free_full(list, (GDestroyNotify)lr_lrfastestmirror_free); *out_list = NULL; } return ret; } static gboolean lr_fastestmirror_perform(GSList *list, gdouble length_of_measurement, LrFastestMirrorCb cb, void *cbdata, GError **err) { assert(!err || *err == NULL); if (!list) return TRUE; CURLM *multihandle = curl_multi_init(); if (!multihandle) { g_set_error(err, LR_FASTESTMIRROR_ERROR, LRE_CURL, "curl_multi_init() error"); return FALSE; } // Add curl easy handles to multi handle long handles_added = 0; for (GSList *elem = list; elem; elem = g_slist_next(elem)) { LrFastestMirror *mirror = elem->data; if (mirror->curl) { curl_multi_add_handle(multihandle, mirror->curl); handles_added++; } } if (handles_added == 0) { curl_multi_cleanup(multihandle); return TRUE; } cb(cbdata, LR_FMSTAGE_DETECTION, (void *) &handles_added); int still_running; gdouble elapsed_time = 0.0; _cleanup_timer_destroy_ GTimer *timer = g_timer_new(); g_timer_start(timer); do { struct timeval timeout; int rc, cm_rc; int maxfd = -1; long curl_timeout = -1; fd_set fdread, fdwrite, fdexcep; FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_ZERO(&fdexcep); // Set suitable timeout to play around with timeout.tv_sec = 0; timeout.tv_usec = HALF_OF_SECOND_IN_MICROS; cm_rc = curl_multi_timeout(multihandle, &curl_timeout); if (cm_rc != CURLM_OK) { g_set_error(err, LR_FASTESTMIRROR_ERROR, LRE_CURLM, "curl_multi_timeout() error: %s", curl_multi_strerror(cm_rc)); curl_multi_cleanup(multihandle); return FALSE; } // Set timeout to a reasonable value if (curl_timeout >= 0) { timeout.tv_sec = curl_timeout / 1000; if (timeout.tv_sec >= 1) { timeout.tv_sec = 0; timeout.tv_usec = HALF_OF_SECOND_IN_MICROS; } else { timeout.tv_usec = (curl_timeout % 1000) * 1000; if (timeout.tv_usec > HALF_OF_SECOND_IN_MICROS) timeout.tv_usec = HALF_OF_SECOND_IN_MICROS; } } // Get file descriptors from the transfers cm_rc = curl_multi_fdset(multihandle, &fdread, &fdwrite, &fdexcep, &maxfd); if (cm_rc != CURLM_OK) { g_set_error(err, LR_FASTESTMIRROR_ERROR, LRE_CURLM, "curl_multi_fdset() error: %s", curl_multi_strerror(cm_rc)); curl_multi_cleanup(multihandle); return FALSE; } rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout); if (rc < 0) { if (errno == EINTR) { g_debug("%s: select() interrupted by signal", __func__); } else { g_set_error(err, LR_FASTESTMIRROR_ERROR, LRE_SELECT, "select() error: %s", g_strerror(errno)); curl_multi_cleanup(multihandle); return FALSE; } } curl_multi_perform(multihandle, &still_running); // Break loop after some reasonable amount of time elapsed_time = g_timer_elapsed(timer, NULL); } while(still_running && elapsed_time < length_of_measurement); // Remove curl easy handles from multi handle // and calculate plain_connect_time for (GSList *elem = list; elem; elem = g_slist_next(elem)) { LrFastestMirror *mirror = elem->data; CURL *curl = mirror->curl; if (!curl) continue; // Remove handle curl_multi_remove_handle(multihandle, curl); // Calculate plain_connect_time char *effective_url; curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effective_url); if (!effective_url) { // No effective url is most likely an error mirror->plain_connect_time = -1.0; } else if (g_str_has_prefix(effective_url, "file:")) { // Local directories are considered to be the best mirrors mirror->plain_connect_time = 0.0; } else { // Get connect time double namelookup_time; double connect_time; double plain_connect_time; curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME, &namelookup_time); curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &connect_time); if (connect_time == 0.0) { // Zero connect time is most likely an error plain_connect_time = -1.0; } else { plain_connect_time = connect_time - namelookup_time; } mirror->plain_connect_time = plain_connect_time; //g_debug("%s: name_lookup: %3.6f connect_time: %3.6f (%3.6f) | %s", // __func__, namelookup_time, connect_time, // mirror->plain_connect_time, mirror->url); } } curl_multi_cleanup(multihandle); return TRUE; } static void null_cb(G_GNUC_UNUSED void *clientp, G_GNUC_UNUSED LrFastestMirrorStages stage, G_GNUC_UNUSED void *ptr) { return; } static gint cmp_fastestmirrors(gconstpointer a, gconstpointer b) { const LrFastestMirror *a_mirror = a; const LrFastestMirror *b_mirror = b; double a_ct = a_mirror->plain_connect_time; double b_ct = b_mirror->plain_connect_time; if (a_ct < 0.0 && b_ct < 0.0) return 0; if (a_ct < 0.0) return 1; if (b_ct < 0.0) return -1; if (a_ct < b_ct) return -1; else if (a_ct == b_ct) return 0; else return 1; } gboolean lr_fastestmirror_detailed(LrHandle *handle, GSList *inlist, GSList **outlist, GError **err) { assert(!err || *err == NULL); char *fastestmirrorcache = NULL; gdouble length_of_measurement = LENGTH_OF_MEASUREMENT; LrFastestMirrorCb cb = null_cb; void *cbdata = NULL; if (handle) { fastestmirrorcache = handle->fastestmirrorcache; if (handle->fastestmirrorcb) cb = handle->fastestmirrorcb; cbdata = handle->fastestmirrordata; length_of_measurement = handle->fastestmirrortimeout; if (handle->offline) { g_debug("%s: Fastest mirror determination " "skipped... LRO_OFFLINE enabled", __func__); return TRUE; } } g_debug("%s: Fastest mirror determination in progress...", __func__); cb(cbdata, LR_FMSTAGE_INIT, NULL); if (!inlist) { cb(cbdata, LR_FMSTAGE_STATUS, NULL); return TRUE; } // Load cache gboolean ret; LrFastestMirrorCache *cache = NULL; ret = lr_fastestmirrorcache_load(&cache, fastestmirrorcache, cb, cbdata, err); if (!ret) { cb(cbdata, LR_FMSTAGE_STATUS, "Cannot load cache"); return FALSE; } // Prepare list of LrFastestMirror elements GSList *lrfastestmirrors; ret = lr_fastestmirror_prepare(handle, inlist, &lrfastestmirrors, cache, err); if (!ret) { cb(cbdata, LR_FMSTAGE_STATUS, "Error while lr_fastestmirror_prepare()"); g_debug("%s: Error while lr_fastestmirror_prepare()", __func__); lr_fastestmirrorcache_free(cache); return FALSE; } ret = lr_fastestmirror_perform(lrfastestmirrors, length_of_measurement, cb, cbdata, err); if (!ret) { cb(cbdata, LR_FMSTAGE_STATUS, "Error while detection"); g_debug("%s: Error while lr_fastestmirror_perform()", __func__); g_slist_free_full(lrfastestmirrors, (GDestroyNotify)lr_lrfastestmirror_free); lr_fastestmirrorcache_free(cache); return FALSE; } cb(cbdata, LR_FMSTAGE_FINISHING, NULL); // Sort the mirrors by the connection time lrfastestmirrors = g_slist_sort(lrfastestmirrors, cmp_fastestmirrors); // Update cache gint64 ts = g_get_real_time() / 1000000; // TimeStamp for (GSList *elem = lrfastestmirrors; elem; elem = g_slist_next(elem)) { LrFastestMirror *mirror = elem->data; if (mirror->cached == FALSE) { lr_fastestmirrorcache_update(cache, mirror->url, ts, mirror->plain_connect_time); } } lr_fastestmirrorcache_write(cache, NULL); lr_fastestmirrorcache_free(cache); *outlist = lrfastestmirrors; cb(cbdata, LR_FMSTAGE_STATUS, NULL); return TRUE; } gboolean lr_fastestmirror(LrHandle *handle, GSList **list, GError **err) { GSList *lrfastestmirrors = NULL; GSList *new_list = NULL; assert(!err || *err == NULL); // Prepare list of LrFastestMirror elements gboolean ret = lr_fastestmirror_detailed(handle, *list, &lrfastestmirrors, err); if (!ret) { g_slist_free_full(lrfastestmirrors, (GDestroyNotify)lr_lrfastestmirror_free); return FALSE; } // Sort the mirrors by the connection time for (GSList *elem = lrfastestmirrors; elem; elem = g_slist_next(elem)) { LrFastestMirror *mirror = elem->data; g_debug("%s: %3.6f : %s", __func__, mirror->plain_connect_time, mirror->url); new_list = g_slist_append(new_list, mirror->url); } g_slist_free_full(lrfastestmirrors, (GDestroyNotify)lr_lrfastestmirror_free); g_slist_free(*list); *list = new_list; return TRUE; } gboolean lr_fastestmirror_sort_internalmirrorlist(LrHandle *handle, GError **err) { assert(!err || *err == NULL); GSList *list = g_slist_prepend(NULL, handle); gboolean ret = lr_fastestmirror_sort_internalmirrorlists(list, err); g_slist_free(list); return ret; } gboolean lr_fastestmirror_sort_internalmirrorlists(GSList *handles, GError **err) { assert(!err || *err == NULL); if (!handles) return TRUE; _cleanup_timer_destroy_ GTimer *timer = g_timer_new(); g_timer_start(timer); LrHandle *main_handle = handles->data; // Network configuration for the // test is used from the first // handle // Prepare list of hosts gchar *fastestmirrorcache = main_handle->fastestmirrorcache; _cleanup_hashtable_unref_ GHashTable *hosts_ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); for (GSList *ehandle = handles; ehandle; ehandle = g_slist_next(ehandle)) { LrHandle *handle = ehandle->data; GSList *mirrors = handle->internal_mirrorlist; for (GSList *elem = mirrors; elem; elem = g_slist_next(elem)) { LrInternalMirror *imirror = elem->data; gchar *host = lr_url_without_path(imirror->url); g_hash_table_insert(hosts_ht, host, NULL); } // Cache related warning if (fastestmirrorcache) { if (handle->fastestmirrorcache && g_strcmp0(fastestmirrorcache, handle->fastestmirrorcache)) g_warning("%s: Multiple fastestmirror caches are specified! " "Used one is %s (%s is ignored)", __func__, fastestmirrorcache, handle->fastestmirrorcache); } else { if (handle->fastestmirrorcache) g_warning("%s: First handle doesn't have a fastestmirror " "cache specified but other one has: %s", __func__, handle->fastestmirrorcache); } } _cleanup_list_free_ GList *tmp_list_of_urls = g_hash_table_get_keys(hosts_ht); _cleanup_slist_free_ GSList *list_of_urls = NULL; int number_of_mirrors = 0; for (GList *elem = tmp_list_of_urls; elem; elem = g_list_next(elem)) { list_of_urls = g_slist_prepend(list_of_urls, elem->data); number_of_mirrors++; } if (number_of_mirrors <= 1) { // Nothing to do return TRUE; } // Sort this list by the connection time gboolean ret = lr_fastestmirror(main_handle, &list_of_urls, err); if (!ret) { g_debug("%s: lr_fastestmirror failed", __func__); return FALSE; } // Apply sorted order to each handle for (GSList *ehandle = handles; ehandle; ehandle = g_slist_next(ehandle)) { LrHandle *handle = ehandle->data; GSList *mirrors = handle->internal_mirrorlist; GSList *new_list = NULL; for (GSList *elem = list_of_urls; elem; elem = g_slist_next(elem)) { gchar *host = elem->data; for (GSList *ime = mirrors; ime; ime = g_slist_next(ime)) { LrInternalMirror *im = ime->data; gchar *im_host = lr_url_without_path(im->url); if (!g_strcmp0(im_host, host)) { new_list = g_slist_prepend(new_list, im); // XXX: Maybe convert GSList to GList to make // this delete more efficient mirrors = g_slist_delete_link(mirrors, ime); break; } } } // If multiple mirrors with the same lr_url_without_path(url) // were present, only the first occurrence was inserted to the // the new_list and removed from the mirrors list. // The remaining occurrences will be moved here. for (GSList *elem = mirrors; elem; elem = g_slist_next(elem)) { LrInternalMirror *im = elem->data; new_list = g_slist_prepend(new_list, im); } g_slist_free(mirrors); // Set sorted list to the handle (reversed, because the items // of the new_list were prepended) handle->internal_mirrorlist = g_slist_reverse(new_list); } g_timer_stop(timer); g_debug("%s: Duration: %f", __func__, g_timer_elapsed(timer, NULL)); return TRUE; } librepo-1.12.1/librepo/fastestmirror.h000066400000000000000000000050571372027637700177660ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_FASTESTMIRROR_H__ #define __LR_FASTESTMIRROR_H__ #include #include #include "url_substitution.h" #include "mirrorlist.h" #include "metalink.h" #include "handle.h" G_BEGIN_DECLS typedef struct { gchar *url; // Points to string passed by the user CURL *curl; // Curl handle or NULL double plain_connect_time; // Mirror connect time (<0.0 if connection was unsuccessful) gboolean cached; // Was connect time load from cache? } LrFastestMirror; /** Free LrFastestMirror */ void lr_lrfastestmirror_free(LrFastestMirror *mirror); /** Sorts list or mirror URLs by their connections times. * @param handle LrHandle or NULL * @param list Pointer to the GSList of urls (char* or gchar*) * that will be sorted. * @param err GError ** * @return TRUE if everything is ok, FALSE if the err is set. */ gboolean lr_fastestmirror(LrHandle *handle, GSList **list, GError **err); /** For list of URLs create a sorted list of LrFastestMirrors * Note: url strings in outlist poins to the strings from inlist! * @param handle LrHandle or NULL * @param inlist GSList of urls (char* or gchar*), this list * stays unchanged * @param outlist GSList of LrFastestMirror* * @param err GError ** * @return TRUE if everything is ok, FLASE if the err is set */ gboolean lr_fastestmirror_detailed(LrHandle *handle, GSList *inlist, GSList **outlist, GError **err); G_END_DECLS #endif librepo-1.12.1/librepo/fastestmirror_internal.h000066400000000000000000000025501372027637700216550ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_FASTESTMIRROR_INTERNAL_H__ #define __LR_FASTESTMIRROR_INTERNAL_H__ #include #include "handle.h" G_BEGIN_DECLS gboolean lr_fastestmirror_sort_internalmirrorlist(LrHandle *handle, GError **err); /** * @param list List of LrHandles */ gboolean lr_fastestmirror_sort_internalmirrorlists(GSList *handles, GError **err); G_END_DECLS #endif librepo-1.12.1/librepo/gpg.c000066400000000000000000000264621372027637700156350ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "rcodes.h" #include "util.h" #include "gpg.h" /* * Creates the '/run/user/$UID' directory if it doesn't exist. If this * directory exists, gpgagent will create its sockets under * '/run/user/$UID/gnupg'. * * If this directory doesn't exist, gpgagent will create its sockets in gpg * home directory, which is under '/var/cache/yum/metadata/' and this was * causing trouble with container images, see [1]. * * Previous solution was to send the agent a "KILLAGENT" message, but that * would cause a race condition with calling gpgme_release(), see [2], [3]. * * Since the agent doesn't clean up its sockets properly, by creating this * directory we make sure they are in a place that is not causing trouble with * container images. * * [1] https://bugzilla.redhat.com/show_bug.cgi?id=1650266 * [2] https://bugzilla.redhat.com/show_bug.cgi?id=1769831 * [3] https://github.com/rpm-software-management/microdnf/issues/50 */ void ensure_socket_dir_exists() { char dirname[32]; snprintf(dirname, sizeof(dirname), "/run/user/%u", getuid()); int res = mkdir(dirname, 0700); if (res != 0 && errno != EEXIST) { g_debug("Failed to create \"%s\": %d - %s\n", dirname, errno, strerror(errno)); } } gboolean lr_gpg_check_signature_fd(int signature_fd, int data_fd, const char *home_dir, GError **err) { gpgme_error_t gpgerr; gpgme_ctx_t context; gpgme_data_t signature_data; gpgme_data_t data_data; gpgme_verify_result_t result; gpgme_signature_t sig; assert(!err || *err == NULL); // Initialization gpgme_check_version(NULL); gpgerr = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP); if (gpgerr != GPG_ERR_NO_ERROR) { g_debug("%s: gpgme_engine_check_version: %s", __func__, gpgme_strerror(gpgerr)); g_set_error(err, LR_GPG_ERROR, LRE_GPGNOTSUPPORTED, "gpgme_engine_check_version() error: %s", gpgme_strerror(gpgerr)); return FALSE; } gpgerr = gpgme_new(&context); if (gpgerr != GPG_ERR_NO_ERROR) { g_debug("%s: gpgme_new: %s", __func__, gpgme_strerror(gpgerr)); g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR, "gpgme_new() error: %s", gpgme_strerror(gpgerr)); return FALSE; } gpgerr = gpgme_set_protocol(context, GPGME_PROTOCOL_OpenPGP); if (gpgerr != GPG_ERR_NO_ERROR) { g_debug("%s: gpgme_set_protocol: %s", __func__, gpgme_strerror(gpgerr)); g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR, "gpgme_set_protocol() error: %s", gpgme_strerror(gpgerr)); gpgme_release(context); return FALSE; } if (home_dir) { gpgerr = gpgme_ctx_set_engine_info(context, GPGME_PROTOCOL_OpenPGP, NULL, home_dir); if (gpgerr != GPG_ERR_NO_ERROR) { g_debug("%s: gpgme_ctx_set_engine_info: %s", __func__, gpgme_strerror(gpgerr)); g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR, "gpgme_ctx_set_engine_info() error: %s", gpgme_strerror(gpgerr)); gpgme_release(context); return FALSE; } } gpgme_set_armor(context, 1); gpgerr = gpgme_data_new_from_fd(&signature_data, signature_fd); if (gpgerr != GPG_ERR_NO_ERROR) { g_debug("%s: gpgme_data_new_from_fd: %s", __func__, gpgme_strerror(gpgerr)); g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR, "gpgme_data_new_from_fd(_, %d) error: %s", signature_fd, gpgme_strerror(gpgerr)); gpgme_release(context); return FALSE; } gpgerr = gpgme_data_new_from_fd(&data_data, data_fd); if (gpgerr != GPG_ERR_NO_ERROR) { g_debug("%s: gpgme_data_new_from_fd: %s", __func__, gpgme_strerror(gpgerr)); g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR, "gpgme_data_new_from_fd(_, %d) error: %s", data_fd, gpgme_strerror(gpgerr)); gpgme_data_release(signature_data); gpgme_release(context); return FALSE; } // Verify gpgerr = gpgme_op_verify(context, signature_data, data_data, NULL); gpgme_data_release(signature_data); gpgme_data_release(data_data); if (gpgerr != GPG_ERR_NO_ERROR) { g_debug("%s: gpgme_op_verify: %s", __func__, gpgme_strerror(gpgerr)); g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR, "gpgme_op_verify() error: %s", gpgme_strerror(gpgerr)); gpgme_release(context); return FALSE; } result = gpgme_op_verify_result(context); if (!result) { g_debug("%s: gpgme_op_verify_result: error", __func__); g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR, "gpgme_op_verify_result() error: %s", gpgme_strerror(gpgerr)); gpgme_release(context); return FALSE; } // Check result of verification sig = result->signatures; if(!sig) { g_debug("%s: signature verify error (no signatures)", __func__); g_set_error(err, LR_GPG_ERROR, LRE_BADGPG, "Signature verify error - no signatures"); gpgme_release(context); return FALSE; } // Example of signature usage could be found in gpgme git repository // in the gpgme/tests/run-verify.c for (; sig; sig = sig->next) { if ((sig->summary & GPGME_SIGSUM_VALID) || // Valid (sig->summary & GPGME_SIGSUM_GREEN) || // Valid (sig->summary == 0 && sig->status == GPG_ERR_NO_ERROR)) // Valid but key is not certified with a trusted signature { gpgme_release(context); return TRUE; } } gpgme_release(context); g_debug("%s: Bad GPG signature", __func__); g_set_error(err, LR_GPG_ERROR, LRE_BADGPG, "Bad GPG signature"); return FALSE; } gboolean lr_gpg_check_signature(const char *signature_fn, const char *data_fn, const char *home_dir, GError **err) { gboolean ret; int signature_fd, data_fd; assert(!err || *err == NULL); signature_fd = open(signature_fn, O_RDONLY); if (signature_fd == -1) { g_debug("%s: Opening signature %s: %s", __func__, signature_fn, g_strerror(errno)); g_set_error(err, LR_GPG_ERROR, LRE_IO, "Error while opening signature %s: %s", signature_fn, g_strerror(errno)); return FALSE; } data_fd = open(data_fn, O_RDONLY); if (data_fd == -1) { g_debug("%s: Opening data %s: %s", __func__, data_fn, g_strerror(errno)); g_set_error(err, LR_GPG_ERROR, LRE_IO, "Error while opening %s: %s", data_fn, g_strerror(errno)); close(signature_fd); return FALSE; } ret = lr_gpg_check_signature_fd(signature_fd, data_fd, home_dir, err); close(signature_fd); close(data_fd); return ret; } gboolean lr_gpg_import_key(const char *key_fn, const char *home_dir, GError **err) { gpgme_error_t gpgerr; int key_fd; gpgme_ctx_t context; gpgme_data_t key_data; assert(!err || *err == NULL); ensure_socket_dir_exists(); // Initialization gpgme_check_version(NULL); gpgerr = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP); if (gpgerr != GPG_ERR_NO_ERROR) { g_debug("%s: gpgme_engine_check_version: %s", __func__, gpgme_strerror(gpgerr)); g_set_error(err, LR_GPG_ERROR, LRE_GPGNOTSUPPORTED, "gpgme_engine_check_version() error: %s", gpgme_strerror(gpgerr)); return FALSE; } gpgerr = gpgme_new(&context); if (gpgerr != GPG_ERR_NO_ERROR) { g_debug("%s: gpgme_new: %s", __func__, gpgme_strerror(gpgerr)); g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR, "gpgme_new() error: %s", gpgme_strerror(gpgerr)); return FALSE; } gpgerr = gpgme_set_protocol(context, GPGME_PROTOCOL_OpenPGP); if (gpgerr != GPG_ERR_NO_ERROR) { g_debug("%s: gpgme_set_protocol: %s", __func__, gpgme_strerror(gpgerr)); g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR, "gpgme_set_protocol() error: %s", gpgme_strerror(gpgerr)); gpgme_release(context); return FALSE; } if (home_dir) { gpgerr = gpgme_ctx_set_engine_info(context, GPGME_PROTOCOL_OpenPGP, NULL, home_dir); if (gpgerr != GPG_ERR_NO_ERROR) { g_debug("%s: gpgme_ctx_set_engine_info: %s", __func__, gpgme_strerror(gpgerr)); g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR, "gpgme_ctx_set_engine_info() error: %s", gpgme_strerror(gpgerr)); gpgme_release(context); return FALSE; } } gpgme_set_armor(context, 1); // Key import key_fd = open(key_fn, O_RDONLY); if (key_fd == -1) { g_debug("%s: Opening key: %s", __func__, g_strerror(errno)); g_set_error(err, LR_GPG_ERROR, LRE_IO, "Error while opening key %s: %s", key_fn, g_strerror(errno)); gpgme_release(context); return FALSE; } gpgerr = gpgme_data_new_from_fd(&key_data, key_fd); if (gpgerr != GPG_ERR_NO_ERROR) { g_debug("%s: gpgme_data_new_from_fd: %s", __func__, gpgme_strerror(gpgerr)); g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR, "gpgme_data_new_from_fd(_, %d) error: %s", key_fd, gpgme_strerror(gpgerr)); gpgme_release(context); close(key_fd); return FALSE; } gpgerr = gpgme_op_import(context, key_data); gpgme_data_release(key_data); if (gpgerr != GPG_ERR_NO_ERROR) { g_debug("%s: gpgme_op_import: %s", __func__, gpgme_strerror(gpgerr)); g_set_error(err, LR_GPG_ERROR, LRE_GPGERROR, "gpgme_op_import() error: %s", gpgme_strerror(gpgerr)); gpgme_release(context); close(key_fd); return FALSE; } close(key_fd); gpgme_release(context); return TRUE; } librepo-1.12.1/librepo/gpg.h000066400000000000000000000055311372027637700156340ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_GPG_H__ #define __LR_GPG_H__ #include G_BEGIN_DECLS /** \defgroup gpg GPG signature verification * \addtogroup gpg * @{ */ /** Check detached signature of data. * @param signature_fd File descriptor of signature file. * @param data_fd File descriptor of data to verify. * @param home_dir Configuration directory of OpenPGP engine * (e.g. "/home/user/.gnupg/"), if NULL default * config directory is used. * @param err GError ** * @return returns TRUE if error is not set and FALSE if it is. */ gboolean lr_gpg_check_signature_fd(int signature_fd, int data_fd, const char *home_dir, GError **err); /** Check detached signature of data. * @param signature_fn Filename (path) of signature file. * @param data_fn Filename (path) of data to verify. * @param home_dir Configuration directory of OpenPGP engine * (e.g. "/home/user/.gnupg/"), if NULL default * config directory is used. * @param err GError ** * @return returns TRUE if error is not set and FALSE if it is. */ gboolean lr_gpg_check_signature(const char *signature_fn, const char *data_fn, const char *home_dir, GError **err); /** Import key into the keyring. * @param key_fn Filename (path) of key file. * @param home_dir Configuration directory of OpenPGP engine * (e.g. "/home/user/.gnupg/"), if NULL default * config directory is used. * @param err GError ** * @return returns TRUE if error is not set and FALSE if it is. */ gboolean lr_gpg_import_key(const char *key_fn, const char *home_dir, GError **err); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/handle.c000066400000000000000000001376751372027637700163240ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _POSIX_C_SOURCE 200809L #define _BSD_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "handle_internal.h" #include "handle.h" #include "result_internal.h" #include "repomd.h" #include "rcodes.h" #include "util.h" #include "yum.h" #include "version.h" #include "yum_internal.h" #include "url_substitution.h" #include "downloader.h" #include "fastestmirror_internal.h" #include "cleanup.h" CURL * lr_get_curl_handle() { CURL *h; lr_global_init(); h = curl_easy_init(); curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(h, CURLOPT_MAXREDIRS, 6); curl_easy_setopt(h, CURLOPT_CONNECTTIMEOUT, LRO_CONNECTTIMEOUT_DEFAULT); curl_easy_setopt(h, CURLOPT_LOW_SPEED_TIME, LRO_LOWSPEEDTIME_DEFAULT); curl_easy_setopt(h, CURLOPT_LOW_SPEED_LIMIT, LRO_LOWSPEEDLIMIT_DEFAULT); curl_easy_setopt(h, CURLOPT_SSL_VERIFYHOST, 2); curl_easy_setopt(h, CURLOPT_SSL_VERIFYPEER, 1); curl_easy_setopt(h, CURLOPT_FTP_USE_EPSV, LRO_FTPUSEEPSV_DEFAULT); curl_easy_setopt(h, CURLOPT_FILETIME, 0); return h; } void lr_handle_free_list(char ***list) { if (!list || *list == NULL) return; for (int x=0; (*list)[x]; x++) lr_free((*list)[x]); lr_free(*list); *list = NULL; } LrHandle * lr_handle_init(void) { LrHandle *handle; CURL *curl = lr_get_curl_handle(); if (!curl) return NULL; handle = lr_malloc0(sizeof(LrHandle)); handle->curl_handle = curl; handle->fastestmirrormaxage = LRO_FASTESTMIRRORMAXAGE_DEFAULT; handle->mirrorlist_fd = -1; handle->metalink_fd = -1; handle->onetimeflag_apply = FALSE; handle->checks |= LR_CHECK_CHECKSUM; handle->maxparalleldownloads = LRO_MAXPARALLELDOWNLOADS_DEFAULT; handle->maxdownloadspermirror = LRO_MAXDOWNLOADSPERMIRROR_DEFAULT; handle->lowspeedtime = LRO_LOWSPEEDTIME_DEFAULT; handle->lowspeedlimit = LRO_LOWSPEEDLIMIT_DEFAULT; handle->sslverifypeer = 1; handle->sslverifyhost = 2; handle->ipresolve = LRO_IPRESOLVE_DEFAULT; handle->allowed_mirror_failures = LRO_ALLOWEDMIRRORFAILURES_DEFAULT; handle->adaptivemirrorsorting = LRO_ADAPTIVEMIRRORSORTING_DEFAULT; handle->gnupghomedir = g_strdup(LRO_GNUPGHOMEDIR_DEFAULT); handle->fastestmirrortimeout = LRO_FASTESTMIRRORTIMEOUT_DEFAULT; handle->offline = LRO_OFFLINE_DEFAULT; handle->httpauthmethods = LRO_HTTPAUTHMETHODS_DEFAULT; handle->proxyauthmethods = LRO_PROXYAUTHMETHODS_DEFAULT; handle->ftpuseepsv = LRO_FTPUSEEPSV_DEFAULT; handle->cachedir = NULL; handle->preservetime = 0; return handle; } void lr_handle_free(LrHandle *handle) { if (!handle) return; if (handle->curl_handle) curl_easy_cleanup(handle->curl_handle); if (handle->mirrorlist_fd != -1) close(handle->mirrorlist_fd); if (handle->metalink_fd != -1) close(handle->metalink_fd); lr_handle_free_list(&handle->urls); lr_free(handle->fastestmirrorcache); lr_free(handle->mirrorlist); lr_free(handle->mirrorlisturl); lr_free(handle->metalinkurl); lr_free(handle->onetimeflag); lr_free(handle->used_mirror); lr_free(handle->destdir); lr_free(handle->useragent); lr_free(handle->sslclientcert); lr_free(handle->sslclientkey); lr_free(handle->sslcacert); lr_lrmirrorlist_free(handle->internal_mirrorlist); lr_lrmirrorlist_free(handle->urls_mirrors); lr_lrmirrorlist_free(handle->mirrorlist_mirrors); lr_lrmirrorlist_free(handle->metalink_mirrors); lr_lrmirrorlist_free(handle->mirrors); lr_metalink_free(handle->metalink); lr_handle_free_list(&handle->yumdlist); lr_urlvars_free(handle->yumslist); lr_handle_free_list(&handle->yumblist); lr_urlvars_free(handle->urlvars); lr_free(handle->gnupghomedir); lr_free(handle->cachedir); lr_handle_free_list(&handle->httpheader); lr_free(handle); } typedef enum { LR_REMOTESOURCE_URLS, LR_REMOTESOURCE_MIRRORLIST, LR_REMOTESOURCE_METALINK, LR_REMOTESOURCE_LOCAL_OPT, LR_REMOTESOURCE_OFFLINE_OPT, LR_REMOTESOURCE_OTHER, } LrChangedRemoteSource; static unsigned long curlauth_bitmask(LrAuth mask) { unsigned long out_mask = 0UL; if (mask == LR_AUTH_NONE) return (unsigned long) CURLAUTH_NONE; if (mask & LR_AUTH_BASIC) out_mask |= CURLAUTH_BASIC; if (mask & LR_AUTH_DIGEST) out_mask |= CURLAUTH_DIGEST; if (mask & LR_AUTH_NEGOTIATE) #ifdef CURLAUTH_NEGOTIATE out_mask |= CURLAUTH_NEGOTIATE; #else out_mask |= CURLAUTH_GSSNEGOTIATE; #endif if (mask & LR_AUTH_NTLM) out_mask |= CURLAUTH_NTLM; if (mask & LR_AUTH_DIGEST_IE) out_mask |= CURLAUTH_DIGEST_IE; if (mask & LR_AUTH_NTLM_WB) out_mask |= CURLAUTH_NTLM_WB; if (mask & LR_AUTH_ONLY) out_mask |= CURLAUTH_ONLY; if (mask == LR_AUTH_ANY) out_mask |= CURLAUTH_ANY; return out_mask; } static void lr_handle_remote_sources_changed(LrHandle *handle, LrChangedRemoteSource type) { // Called when options like: // LRO_URLS // LRO_MIRRORLIST // LRO_MIRRORLISTURL // LRO_METALINKURL // LRO_LOCAL // LRO_OFFLINE // are changed // Internal mirrorlist is no more valid lr_lrmirrorlist_free(handle->internal_mirrorlist); handle->internal_mirrorlist = NULL; // Mirrors reported via mirrors are no more valid too lr_lrmirrorlist_free(handle->mirrors); handle->mirrors = NULL; if (type == LR_REMOTESOURCE_URLS) { lr_lrmirrorlist_free(handle->urls_mirrors); handle->urls_mirrors = NULL; } if (type == LR_REMOTESOURCE_MIRRORLIST) { lr_lrmirrorlist_free(handle->mirrorlist_mirrors); handle->mirrorlist_mirrors = NULL; if (handle->mirrorlist_fd != -1) close(handle->mirrorlist_fd); handle->mirrorlist_fd = -1; } if (type == LR_REMOTESOURCE_METALINK) { lr_lrmirrorlist_free(handle->metalink_mirrors); handle->metalink_mirrors = NULL; if (handle->metalink_fd != -1) close(handle->metalink_fd); handle->metalink_fd = -1; lr_metalink_free(handle->metalink); handle->metalink = NULL; } } gboolean lr_handle_setopt(LrHandle *handle, GError **err, LrHandleOption option, ...) { gboolean ret = TRUE; va_list arg; CURLcode c_rc = CURLE_OK; CURL *c_h; assert(!err || *err == NULL); // Variables for values from va_arg long val_long; gint64 val_gint64; if (!handle) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADFUNCARG, "No handle specified"); return FALSE; } c_h = handle->curl_handle; va_start(arg, option); switch (option) { case LRO_UPDATE: handle->update = va_arg(arg, long) ? 1 : 0; break; case LRO_MIRRORLIST: // DEPRECATED! g_warning("WARNING! Deprecated LRO_MIRRORLIST used"); if (handle->mirrorlist) lr_free(handle->mirrorlist); handle->mirrorlist = g_strdup(va_arg(arg, char *)); if (handle->mirrorlisturl) lr_free(handle->mirrorlisturl); handle->mirrorlisturl = NULL; if (handle->metalinkurl) lr_free(handle->metalinkurl); handle->metalinkurl = NULL; lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_MIRRORLIST); lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_METALINK); if (!handle->mirrorlist) break; if (strstr(handle->mirrorlist, "metalink")) handle->metalinkurl = g_strdup(handle->mirrorlist); else handle->mirrorlisturl = g_strdup(handle->mirrorlist); break; case LRO_MIRRORLISTURL: if (handle->mirrorlisturl) lr_free(handle->mirrorlisturl); handle->mirrorlisturl = g_strdup(va_arg(arg, char *)); lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_MIRRORLIST); break; case LRO_METALINKURL: if (handle->metalinkurl) lr_free(handle->metalinkurl); handle->metalinkurl = g_strdup(va_arg(arg, char *)); lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_METALINK); break; case LRO_ONETIMEFLAG: if (handle->onetimeflag) lr_free(handle->onetimeflag); handle->onetimeflag = g_strdup(va_arg(arg, char *)); break; case LRO_LOCAL: lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_LOCAL_OPT); handle->local = va_arg(arg, long) ? 1 : 0; break; case LRO_HTTPAUTH: if (va_arg(arg, long) == 0) { c_rc = curl_easy_setopt(c_h, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); handle->httpauthmethods = LR_AUTH_BASIC; } else { c_rc = curl_easy_setopt(c_h, CURLOPT_HTTPAUTH, CURLAUTH_ANY); handle->httpauthmethods = LR_AUTH_ANY; } break; case LRO_USERPWD: c_rc = curl_easy_setopt(c_h, CURLOPT_USERPWD, va_arg(arg, char *)); break; case LRO_PROXY: c_rc = curl_easy_setopt(c_h, CURLOPT_PROXY, va_arg(arg, char *)); break; case LRO_PROXYPORT: { c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYPORT,va_arg(arg, long)); break; } case LRO_PROXYTYPE: { long curl_proxy = -1; switch (va_arg(arg, long)) { case LR_PROXY_HTTP: curl_proxy = CURLPROXY_HTTP; break; case LR_PROXY_HTTP_1_0: curl_proxy = CURLPROXY_HTTP_1_0;break; case LR_PROXY_SOCKS4: curl_proxy = CURLPROXY_SOCKS4; break; case LR_PROXY_SOCKS5: curl_proxy = CURLPROXY_SOCKS5; break; case LR_PROXY_SOCKS4A: curl_proxy = CURLPROXY_SOCKS4A; break; case LR_PROXY_SOCKS5_HOSTNAME: curl_proxy = CURLPROXY_SOCKS5_HOSTNAME; break; default: break; } if (curl_proxy == -1) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG, "Bad LRO_PROXYTYPE value"); ret = FALSE; } else { c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYTYPE, curl_proxy); } break; } case LRO_PROXYAUTH: if (va_arg(arg, long) == 0) { c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); handle->proxyauthmethods = LR_AUTH_BASIC; } else { c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYAUTH, CURLAUTH_ANY); handle->proxyauthmethods = LR_AUTH_ANY; } break; case LRO_PROXYUSERPWD: c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYUSERPWD, va_arg(arg, char *)); break; case LRO_PROGRESSCB: handle->user_cb = va_arg(arg, LrProgressCb); break; case LRO_PROGRESSDATA: handle->user_data = va_arg(arg, void *); break; case LRO_MAXSPEED: val_gint64 = va_arg(arg, gint64); if (val_gint64 < 0) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG, "Bad value of LRO_MAXSPEED"); ret = FALSE; break; } else if (val_gint64 != 0 && val_gint64 < handle->lowspeedlimit) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG, "LRO_MAXSPEED (%"G_GINT64_FORMAT") is lower than " "LRO_LOWSPEEDLIMIT (%ld)", val_gint64, handle->lowspeedlimit); ret = FALSE; break; } handle->maxspeed = val_gint64; break; case LRO_DESTDIR: if (handle->destdir) lr_free(handle->destdir); handle->destdir = g_strdup(va_arg(arg, char *)); break; case LRO_REPOTYPE: handle->repotype = va_arg(arg, LrRepotype); if (handle->repotype != LR_YUMREPO) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG, "Bad value of LRO_REPOTYPE"); ret = FALSE; } break; case LRO_CONNECTTIMEOUT: c_rc = curl_easy_setopt(c_h, CURLOPT_CONNECTTIMEOUT, va_arg(arg, long)); break; case LRO_IGNOREMISSING: handle->ignoremissing = va_arg(arg, long) ? 1 : 0; break; case LRO_INTERRUPTIBLE: handle->interruptible = va_arg(arg, long) ? 1 : 0; break; case LRO_USERAGENT: { char *useragent = va_arg(arg, char *); if (handle->useragent) lr_free(handle->useragent); handle->useragent = g_strdup(useragent); c_rc = curl_easy_setopt(c_h, CURLOPT_USERAGENT, useragent); break; } case LRO_GPGCHECK: if (va_arg(arg, long)) handle->checks |= LR_CHECK_GPG; else handle->checks &= ~LR_CHECK_GPG; break; case LRO_CHECKSUM: if (va_arg(arg, long)) handle->checks |= LR_CHECK_CHECKSUM; else handle->checks &= ~LR_CHECK_CHECKSUM; break; case LRO_URLS: case LRO_YUMDLIST: case LRO_YUMBLIST: { int size = 0; char **list = va_arg(arg, char **); char ***handle_list = NULL; if (option == LRO_URLS) { handle_list = &handle->urls; lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_URLS); } else if (option == LRO_YUMDLIST) { handle_list = &handle->yumdlist; } else if (option == LRO_YUMBLIST) { handle_list = &handle->yumblist; } lr_handle_free_list(handle_list); if (!list) break; // Get list length while (list[size]) size++; size++; if (size == 1 && option == LRO_URLS) { // Only NULL present in list of URLs, keep handle->urls = NULL break; } // Copy the list *handle_list = lr_strv_dup(list); break; } case LRO_HTTPHEADER: { char **list = va_arg(arg, char **); lr_handle_free_list(&handle->httpheader); handle->httpheader = lr_strv_dup(list); break; } case LRO_FETCHMIRRORS: handle->fetchmirrors = va_arg(arg, long) ? 1 : 0; break; case LRO_MAXMIRRORTRIES: val_long = va_arg(arg, long); if (handle->maxmirrortries < LRO_MAXMIRRORTRIES_MIN) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG, "Value of LRO_MAXMIRRORTRIES is too low (use value > %ld)", LRO_MAXMIRRORTRIES_MIN); ret = FALSE; } else { handle->maxmirrortries = val_long; } break; case LRO_MAXPARALLELDOWNLOADS: val_long = va_arg(arg, long); if (val_long < LRO_MAXPARALLELDOWNLOADS_MIN || val_long > LRO_MAXPARALLELDOWNLOADS_MAX) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG, "Bad value of LRO_MAXPARALLELDOWNLOADS."); ret = FALSE; } else { handle->maxparalleldownloads = val_long; } break; case LRO_MAXDOWNLOADSPERMIRROR: val_long = va_arg(arg, long); if (val_long < LRO_MAXDOWNLOADSPERMIRROR_MIN) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG, "Value of LRO_MAXDOWNLOADSPERMIRROR is too low."); ret = FALSE; } else { handle->maxdownloadspermirror = val_long; } break; case LRO_YUMSLIST: { LrUrlVars *vars = va_arg(arg, LrUrlVars *); lr_urlvars_free(handle->yumslist); handle->yumslist = vars; break; } case LRO_VARSUB: { LrUrlVars *vars = va_arg(arg, LrUrlVars *); lr_urlvars_free(handle->urlvars); handle->urlvars = vars; /* Do not do copy for (LrUrlVars *elem = vars; elem; elem = lr_list_next(elem)) { LrVar *var = elem->data; handle->urlvars = lr_urlvars_set(handle->urlvars, var->var, var->val); } */ break; } case LRO_FASTESTMIRROR: handle->fastestmirror = va_arg(arg, long) ? 1 : 0; break; case LRO_FASTESTMIRRORCACHE: { char *fastestmirrorcache = va_arg(arg, char *); if (handle->fastestmirrorcache) lr_free(handle->fastestmirrorcache); handle->fastestmirrorcache = g_strdup(fastestmirrorcache); break; } case LRO_FASTESTMIRRORMAXAGE: val_long = va_arg(arg, long); if (val_long < LRO_FASTESTMIRRORMAXAGE_MIN) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG, "Value of LRO_FASTESTMIRRORMAXAGE is too low."); ret = FALSE; } else { handle->fastestmirrormaxage = val_long; } break; case LRO_FASTESTMIRRORCB: handle->fastestmirrorcb = va_arg(arg, LrFastestMirrorCb); break; case LRO_FASTESTMIRRORDATA: handle->fastestmirrordata = va_arg(arg, void *); break; case LRO_LOWSPEEDTIME: val_long = va_arg(arg, long); if (val_long < LRO_LOWSPEEDTIME_MIN) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG, "Value of LRO_LOWSPEEDTIME is too low."); ret = FALSE; } else { curl_easy_setopt(c_h, CURLOPT_LOW_SPEED_TIME, val_long); handle->lowspeedtime = val_long; } break; case LRO_LOWSPEEDLIMIT: val_long = va_arg(arg, long); if (val_long < LRO_LOWSPEEDLIMIT_MIN) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG, "Value of LRO_LOWSPEEDLIMIT is too low."); ret = FALSE; } else if (handle->maxspeed != 0 && handle->maxspeed < val_long) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG, "Value of LRO_LOWSPEEDLIMIT (%ld) is higher than " "LRO_MAXSPEED (%"G_GINT64_FORMAT")", val_long, handle->maxspeed); ret = FALSE; } else { curl_easy_setopt(c_h, CURLOPT_LOW_SPEED_LIMIT, val_long); handle->lowspeedlimit = val_long; } break; case LRO_HMFCB: handle->hmfcb = va_arg(arg, LrHandleMirrorFailureCb); break; case LRO_SSLVERIFYPEER: handle->sslverifypeer = va_arg(arg, long) ? 1 : 0; c_rc = curl_easy_setopt(c_h, CURLOPT_SSL_VERIFYPEER, handle->sslverifypeer); break; case LRO_SSLVERIFYHOST: handle->sslverifyhost = va_arg(arg, long) ? 2 : 0; c_rc = curl_easy_setopt(c_h, CURLOPT_SSL_VERIFYHOST, handle->sslverifyhost); break; case LRO_SSLCLIENTCERT: if (handle->sslclientcert) lr_free(handle->sslclientcert); handle->sslclientcert = g_strdup(va_arg(arg, char *)); c_rc = curl_easy_setopt(c_h, CURLOPT_SSLCERT, handle->sslclientcert); break; case LRO_SSLCLIENTKEY: if (handle->sslclientkey) lr_free(handle->sslclientkey); handle->sslclientkey = g_strdup(va_arg(arg, char *)); c_rc = curl_easy_setopt(c_h, CURLOPT_SSLKEY, handle->sslclientkey); break; case LRO_SSLCACERT: if (handle->sslcacert) lr_free(handle->sslcacert); handle->sslcacert = g_strdup(va_arg(arg, char *)); c_rc = curl_easy_setopt(c_h, CURLOPT_CAINFO, handle->sslcacert); break; case LRO_IPRESOLVE: { long type = -1; long lr_type = va_arg(arg, LrIpResolveType); switch (lr_type) { case LR_IPRESOLVE_WHATEVER: type = CURL_IPRESOLVE_WHATEVER; break; case LR_IPRESOLVE_V4: type = CURL_IPRESOLVE_V4; break; case LR_IPRESOLVE_V6: type = CURL_IPRESOLVE_V6; break; default: break; } if (type == -1) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG, "Bad LRO_IPRESOLVE value"); ret = FALSE; } else { handle->ipresolve = lr_type; c_rc = curl_easy_setopt(c_h, CURLOPT_IPRESOLVE, type); } break; } case LRO_ALLOWEDMIRRORFAILURES: handle->allowed_mirror_failures = va_arg(arg, long); break; case LRO_ADAPTIVEMIRRORSORTING: handle->adaptivemirrorsorting = va_arg(arg, long); break; case LRO_GNUPGHOMEDIR: { char *gnupghomedir = va_arg(arg, char *); lr_free(handle->gnupghomedir); handle->gnupghomedir = g_strdup(gnupghomedir); break; } case LRO_FASTESTMIRRORTIMEOUT: handle->fastestmirrortimeout = va_arg(arg, double); break; case LRO_OFFLINE: lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_OFFLINE_OPT); handle->offline = va_arg(arg, long) ? 1 : 0; break; case LRO_HTTPAUTHMETHODS: { LrAuth in_bitmask = va_arg(arg, LrAuth); long bitmask = curlauth_bitmask(in_bitmask); handle->httpauthmethods = in_bitmask; c_rc = curl_easy_setopt(c_h, CURLOPT_HTTPAUTH, bitmask); break; } case LRO_PROXYAUTHMETHODS: { LrAuth in_bitmask = va_arg(arg, LrAuth); long bitmask = curlauth_bitmask(in_bitmask); handle->proxyauthmethods = in_bitmask; c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYAUTH, bitmask); break; } case LRO_FTPUSEEPSV: handle->ftpuseepsv = va_arg(arg, long) ? 1 : 0; c_rc = curl_easy_setopt(c_h, CURLOPT_FTP_USE_EPSV, handle->ftpuseepsv); break; case LRO_CACHEDIR: if (handle->cachedir) lr_free(handle->cachedir); handle->cachedir = g_strdup(va_arg(arg, char *)); break; case LRO_PRESERVETIME: handle->preservetime = va_arg(arg, long) ? 1 : 0; c_rc = curl_easy_setopt(c_h, CURLOPT_FILETIME, handle->preservetime); break; default: g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG, "Unknown option"); ret = FALSE; break; }; /* Handle CURL error return code */ if (ret == TRUE && c_rc != CURLE_OK) { ret = FALSE; switch (c_rc) { case CURLE_FAILED_INIT: g_set_error(err, LR_HANDLE_ERROR, LRE_CURLSETOPT, "curl_easy_setopt error: %s", curl_easy_strerror(c_rc)); break; default: g_set_error(err, LR_HANDLE_ERROR, LRE_CURL, "curl error: %s", curl_easy_strerror(c_rc)); break; }; } va_end(arg); return ret; } static gboolean lr_handle_prepare_urls(LrHandle *handle, GError **err) { assert(!handle->urls_mirrors); for (int x=0; handle->urls[x]; x++) { gchar *url = handle->urls[x]; _cleanup_free_ gchar *final_url = NULL; // Make sure that url has protocol specified final_url = lr_prepend_url_protocol(url); if (!final_url) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADURL, "Cannot resolve path for: \"%s\"", url); return FALSE; } // Append the url into internal list of urls specified by LRO_URLS handle->urls_mirrors = lr_lrmirrorlist_append_url( handle->urls_mirrors, final_url, handle->urlvars); } return TRUE; } static gboolean lr_yum_download_url_retry(int attempts, LrHandle *lr_handle, const char *url, int fd, gboolean no_cache, gboolean is_zchunk, GError **err) { gboolean ret = FALSE; for (int i = 1;; i++) { ret = lr_yum_download_url(lr_handle, url, fd, no_cache, is_zchunk, err); if (ret) return ret; if (i >= attempts) return ret; // Caller to handle the last err g_debug("%s: Attempt #%d to download %s failed: %s", __func__, i, url, (*err)->message); ftruncate(fd, 0); g_clear_error (err); } } static gboolean lr_handle_prepare_mirrorlist(LrHandle *handle, gchar *localpath, GError **err) { assert(handle->mirrorlist_fd == -1); assert(!handle->mirrorlist_mirrors); int fd = -1; // Get file descriptor with content if (!localpath && !handle->mirrorlisturl) { // Nothing to do return TRUE; } else if (localpath && !handle->mirrorlisturl) { // Just try to use mirrorlist of the local repository _cleanup_free_ gchar *path; path = lr_pathconcat(localpath, "mirrorlist", NULL); if (g_file_test(path, G_FILE_TEST_IS_REGULAR)) { g_debug("%s: Local mirrorlist found at %s", __func__, path); fd = open(path, O_RDONLY); if (fd < 0) { g_set_error(err, LR_HANDLE_ERROR, LRE_IO, "Cannot open %s: %s", path, g_strerror(errno)); return FALSE; } } else { // No local mirrorlist return TRUE; } } else if (!handle->mirrorlisturl) { // Nothing to do return TRUE; } else if (handle->offline && !lr_is_local_path(handle->mirrorlisturl)) { // We should work offline, ignore remote mirrorlist g_debug("%s: LRO_OFFLINE used, remote mirrorlist ignored: %s", __func__, handle->mirrorlisturl); return TRUE; } else if (handle->local && !lr_is_local_path(handle->mirrorlisturl)) { // We should work only locally, ignore remote mirrorlist g_debug("%s: LRO_LOCAL used, remote mirrorlist ignored: %s", __func__, handle->mirrorlisturl); return TRUE; } else if (handle->mirrorlisturl) { // Download remote mirrorlist _cleanup_free_ gchar *url = NULL; fd = lr_gettmpfile(); if (fd < 0) { g_debug("%s: Cannot create a temporary file", __func__); g_set_error(err, LR_HANDLE_ERROR, LRE_IO, "Cannot create a temporary file"); return FALSE; } url = lr_prepend_url_protocol(handle->mirrorlisturl); handle->onetimeflag_apply = TRUE; if (!lr_yum_download_url_retry(3, handle, url, fd, TRUE, FALSE, err)) { close(fd); return FALSE; } if (lseek(fd, 0, SEEK_SET) != 0) { g_debug("%s: Seek error: %s", __func__, g_strerror(errno)); g_set_error(err, LR_HANDLE_ERROR, LRE_IO, "lseek(%d, 0, SEEK_SET) error: %s", fd, g_strerror(errno)); close(fd); return FALSE; } } assert(fd >= 0); // Parse the file descriptor content g_debug("%s: Parsing mirrorlist", __func__); LrMirrorlist *ml = lr_mirrorlist_init(); gboolean ret = lr_mirrorlist_parse_file(ml, fd, err); if (!ret) { g_debug("%s: Error while parsing mirrorlist", __func__); close(fd); lr_mirrorlist_free(ml); return FALSE; } if (!ml->urls) { g_debug("%s: No URLs in mirrorlist", __func__); g_set_error(err, LR_HANDLE_ERROR, LRE_MLBAD, "No URLs in mirrorlist"); close(fd); lr_mirrorlist_free(ml); return FALSE; } // List parsed mirrors g_debug("%s: Mirrors from mirrorlist:", __func__); for (GSList *elem = ml->urls; elem; elem = g_slist_next(elem)) g_debug(" %s", (gchar *) elem->data); // Convert mirrorlist to internal mirrorlist format handle->mirrorlist_mirrors = lr_lrmirrorlist_append_mirrorlist( NULL, ml, handle->urlvars); handle->mirrorlist_fd = fd; lr_mirrorlist_free(ml); g_debug("%s: Mirrorlist parsed", __func__); return TRUE; } static gboolean lr_handle_prepare_metalink(LrHandle *handle, gchar *localpath, GError **err) { assert(handle->metalink_fd == -1); assert(!handle->metalink_mirrors); assert(!handle->metalink); int fd = -1; // Get file descriptor with content if (!localpath && !handle->metalinkurl) { // Nothing to do return TRUE; } else if (localpath && !handle->metalinkurl) { // Just try to use metalink of the local repository _cleanup_free_ gchar *path; path = lr_pathconcat(localpath, "metalink.xml", NULL); if (g_file_test(path, G_FILE_TEST_IS_REGULAR)) { g_debug("%s: Local metalink.xml found at %s", __func__, path); fd = open(path, O_RDONLY); if (fd < 0) { g_set_error(err, LR_HANDLE_ERROR, LRE_IO, "Cannot open %s: %s", path, g_strerror(errno)); return FALSE; } } else { // No local metalink return TRUE; } } else if (!handle->metalinkurl) { // Nothing to do return TRUE; } else if (handle->offline && !lr_is_local_path(handle->metalinkurl)) { // We should work offline, ignore remote mirrorlist g_debug("%s: LRO_OFFLINE used, remote metalink ignored: %s", __func__, handle->metalinkurl); return TRUE; } else if (handle->local && !lr_is_local_path(handle->metalinkurl)) { // We should work only locally, ignore remote mirrorlist g_debug("%s: LRO_LOCAL used, remote metalink ignored: %s", __func__, handle->metalinkurl); return TRUE; } else if (handle->metalinkurl) { // Download remote metalink _cleanup_free_ gchar *url = NULL; fd = lr_gettmpfile(); if (fd < 0) { g_debug("%s: Cannot create a temporary file", __func__); g_set_error(err, LR_HANDLE_ERROR, LRE_IO, "Cannot create a temporary file"); return FALSE; } url = lr_prepend_url_protocol(handle->metalinkurl); handle->onetimeflag_apply = TRUE; if (!lr_yum_download_url_retry(3, handle, url, fd, TRUE, FALSE, err)) { close(fd); return FALSE; } if (lseek(fd, 0, SEEK_SET) != 0) { g_debug("%s: Seek error: %s", __func__, g_strerror(errno)); g_set_error(err, LR_HANDLE_ERROR, LRE_IO, "lseek(%d, 0, SEEK_SET) error: %s", fd, g_strerror(errno)); close(fd); return FALSE; } } assert(fd >= 0); // Parse the file descriptor content g_debug("%s: Parsing metalink.xml", __func__); gchar *metalink_file = ""; gchar *metalink_suffix = NULL; if (handle->repotype == LR_YUMREPO) { metalink_file = "repomd.xml"; metalink_suffix = "repodata/repomd.xml"; } LrMetalink *ml = lr_metalink_init(); gboolean ret = lr_metalink_parse_file(ml, fd, metalink_file, lr_xml_parser_warning_logger, "Metalink xml parser", err); if (!ret) { g_warning("Error while parsing metalink"); close(fd); lr_metalink_free(ml); return FALSE; } if (!ml->urls) { g_debug("%s: No URLs in metalink", __func__); g_set_error(err, LR_HANDLE_ERROR, LRE_MLBAD, "No URLs in metalink"); close(fd); lr_metalink_free(ml); return FALSE; } // List parsed mirrors g_debug("%s: Mirrors from metalink:", __func__); for (GSList *elem = ml->urls; elem; elem = g_slist_next(elem)) g_debug(" %s", ((LrMetalinkUrl *) elem->data)->url); // Convert metalink to internal mirrorlist format handle->metalink_mirrors = lr_lrmirrorlist_append_metalink( NULL, ml, metalink_suffix, handle->urlvars); handle->metalink_fd = fd; handle->metalink = ml; g_debug("%s: Metalink parsed", __func__); return TRUE; } gboolean lr_handle_prepare_internal_mirrorlist(LrHandle *handle, gboolean usefastestmirror, GError **err) { assert(!err || *err == NULL); if (handle->internal_mirrorlist) return TRUE; // Internal mirrorlist already exists // Create internal mirrorlist g_debug("%s: Preparing internal mirrorlist", __func__); // Get local path in case of local repository gchar *local_path = NULL; if (handle->urls && handle->urls[0]) { // If first base URL is local path, get that path // (without file: or file:// prefix if specified) gchar *url = handle->urls[0]; if (g_str_has_prefix(url, "file://")) local_path = url + 7; else if (g_str_has_prefix(url, "file:")) // RFC 3986 local_path = url + 5; else if (!strstr(url, "://")) local_path = url; } // Since urls, mirrorlists and metalinks are not exclusive its // sufficient for at least on of the three to succeed in order to // continue. In the case where all of present ones fail we propagate // only the last error, because GError cannot handle multiple // errors at the same time. gboolean ret_urls = FALSE; gboolean ret_mirrorlist = FALSE; gboolean ret_metalink = FALSE; gboolean at_least_one_present = FALSE; GError *tmp_err = NULL; // LRO_URLS if (!handle->urls_mirrors && handle->urls) { at_least_one_present = TRUE; ret_urls = lr_handle_prepare_urls(handle, &tmp_err); if (!ret_urls) { assert(tmp_err); g_warning("LRO_URLS processing failed: %s", tmp_err->message); } } // LRO_MIRRORLISTURL if (!handle->mirrorlist_mirrors && (handle->mirrorlisturl || local_path)) { g_clear_error(&tmp_err); at_least_one_present = TRUE; ret_mirrorlist = lr_handle_prepare_mirrorlist(handle, local_path, &tmp_err); if (!ret_mirrorlist) { assert(tmp_err); g_warning("LRO_MIRRORLISTURL processing failed: %s", tmp_err->message); } } // LRO_METALINKURL if (!handle->metalink_mirrors && (handle->metalinkurl || local_path)) { g_clear_error(&tmp_err); at_least_one_present = TRUE; ret_metalink = lr_handle_prepare_metalink(handle, local_path, &tmp_err); if (!ret_metalink) { assert(tmp_err); g_warning("LRO_METALINKURL processing failed: %s", tmp_err->message); } } if (!ret_urls && !ret_mirrorlist && !ret_metalink && at_least_one_present) { g_propagate_error(err, tmp_err); return FALSE; } // Append all the mirrorlist to the single internal mirrorlist // This internal mirrorlist is used for downloading // Note: LRO_MIRRORLISTURL and LRO_METALINKURL lists are included // to this list only if they are explicitly specified (lists // implicitly loaded from a local repository are not included) g_debug("%s: Finalizing internal mirrorlist", __func__); // Mirrorlist from the LRO_MIRRORLISTURL if (handle->mirrorlisturl) handle->internal_mirrorlist = lr_lrmirrorlist_append_lrmirrorlist( handle->internal_mirrorlist, handle->mirrorlist_mirrors); // Mirrorlist from the LRO_METALINKURL if (handle->metalinkurl) handle->internal_mirrorlist = lr_lrmirrorlist_append_lrmirrorlist( handle->internal_mirrorlist, handle->metalink_mirrors); // Mirrorlist from the LRO_URLS handle->internal_mirrorlist = lr_lrmirrorlist_append_lrmirrorlist( handle->internal_mirrorlist, handle->urls_mirrors); // If enabled, sort internal mirrorlist by the connection // speed (the LRO_FASTESTMIRROR option) if (usefastestmirror) { g_debug("%s: Sorting internal mirrorlist by connection speed", __func__); gboolean ret = lr_fastestmirror_sort_internalmirrorlist(handle, err); if (!ret) return FALSE; } // Prepare mirrors (the list that is reported via LRI_MIRRORS) // This list contains mirrors from mirrorlist and/or metalink // even if they are not explicitly specified, but are available // in a local repo (that is specified at the first url of LRO_URLS) g_debug("%s: Finalizing mirrors reported via LRI_MIRRORS", __func__); handle->mirrors = lr_lrmirrorlist_append_lrmirrorlist( handle->mirrors, handle->mirrorlist_mirrors); handle->mirrors = lr_lrmirrorlist_append_lrmirrorlist( handle->mirrors, handle->metalink_mirrors); return TRUE; } gboolean lr_handle_perform(LrHandle *handle, LrResult *result, GError **err) { int ret = TRUE; GError *tmp_err = NULL; assert(handle); assert(!err || *err == NULL); if (!result) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADFUNCARG, "No result argument passed"); return FALSE; } if (!handle->urls && !handle->mirrorlisturl && !handle->metalinkurl) { g_set_error(err, LR_HANDLE_ERROR, LRE_NOURL, "No LRO_URLS, LRO_MIRRORLISTURL nor LRO_METALINKURL specified"); return FALSE; } if (handle->repotype != LR_YUMREPO) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADFUNCARG, "Bad LRO_REPOTYPE specified"); return FALSE; } /* Setup destination directory */ if (handle->update) { if (!result->destdir) { g_set_error(err, LR_HANDLE_ERROR, LRE_INCOMPLETERESULT, "Incomplete result object, destdir is missing"); return FALSE; } lr_free(handle->destdir); handle->destdir = g_strdup(result->destdir); } else if (!handle->destdir && !handle->local) { handle->destdir = g_strdup(TMP_DIR_TEMPLATE); if (!mkdtemp(handle->destdir)) { g_set_error(err, LR_HANDLE_ERROR, LRE_CANNOTCREATETMP, "Cannot create tmpdir: %s", g_strerror(errno)); return FALSE; } } g_debug("%s: Using dir: %s", __func__, handle->destdir); struct sigaction old_sigact; if (handle->interruptible) { /* Setup sighandler */ struct sigaction sigact; g_debug("%s: Using own SIGINT handler", __func__); memset(&sigact, 0, sizeof(old_sigact)); memset(&sigact, 0, sizeof(sigact)); sigemptyset(&sigact.sa_mask); sigact.sa_handler = lr_sigint_handler; sigaddset(&sigact.sa_mask, SIGINT); sigact.sa_flags = 0; if (sigaction(SIGINT, &sigact, &old_sigact) == -1) { g_set_error(err, LR_HANDLE_ERROR, LRE_SIGACTION, "sigaction(SIGINT,,) error"); return FALSE; } } ret = lr_handle_prepare_internal_mirrorlist(handle, handle->fastestmirror, &tmp_err); if (!ret) { g_debug("Cannot prepare internal mirrorlist: %s", tmp_err->message); g_propagate_prefixed_error(err, tmp_err, "Cannot prepare internal mirrorlist: "); return FALSE; } if (handle->fetchmirrors) { /* Only download and parse mirrorlist */ g_debug("%s: Only fetching mirrorlist/metalink", __func__); } else { /* Do the other stuff */ switch (handle->repotype) { case LR_YUMREPO: g_debug("%s: Downloading/Locating yum repo", __func__); ret = lr_yum_perform(handle, result, &tmp_err); break; default: g_debug("%s: Bad repo type", __func__); assert(0); ret = FALSE; g_set_error(err, LR_HANDLE_ERROR, LRE_BADFUNCARG, "Bad repo type: %d", handle->repotype); break; } } if (handle->interruptible) { /* Restore signal handler */ g_debug("%s: Restoring an old SIGINT handler", __func__); sigaction(SIGINT, &old_sigact, NULL); if (lr_interrupt) { g_set_error(err, LR_HANDLE_ERROR, LRE_INTERRUPTED, "Librepo was interrupted by a signal"); if (tmp_err) g_error_free(tmp_err); return FALSE; } } assert((ret && !tmp_err) || (!ret && tmp_err)); if (tmp_err) g_propagate_error(err, tmp_err); return ret; } gboolean lr_handle_getinfo(LrHandle *handle, GError **err, LrHandleInfoOption option, ...) { gboolean rc = TRUE; va_list arg; char **str; long *lnum; double *dnum; assert(!err || *err == NULL); if (!handle) { g_set_error(err, LR_HANDLE_ERROR, LRE_BADFUNCARG, "No handle specified"); return FALSE; } va_start(arg, option); switch (option) { case LRI_UPDATE: lnum = va_arg(arg, long *); *lnum = (long) handle->update; break; case LRI_MIRRORLIST: str = va_arg(arg, char **); *str = handle->mirrorlist; break; case LRI_MIRRORLISTURL: str = va_arg(arg, char **); *str = handle->mirrorlisturl; break; case LRI_METALINKURL: str = va_arg(arg, char **); *str = handle->metalinkurl; break; case LRI_LOCAL: lnum = va_arg(arg, long *); *lnum = (long) handle->local; break; case LRI_PROGRESSCB: { LrProgressCb *cb= va_arg(arg, LrProgressCb *); *cb = handle->user_cb; break; } case LRI_PROGRESSDATA: { void **data = va_arg(arg, void **); *data = handle->user_data; break; } case LRI_DESTDIR: str = va_arg(arg, char **); *str = handle->destdir; break; case LRI_REPOTYPE: lnum = va_arg(arg, long *); *lnum = (long) handle->repotype; break; case LRI_USERAGENT: str = va_arg(arg, char **); *str = handle->useragent; break; case LRI_URLS: case LRI_YUMDLIST: case LRI_YUMBLIST: case LRI_HTTPHEADER: { char **source_list = NULL; char ***strlist = va_arg(arg, char ***); if (option == LRI_URLS) source_list = handle->urls; else if (option == LRI_YUMDLIST) source_list = handle->yumdlist; else if (option == LRI_YUMBLIST) source_list = handle->yumblist; else if (option == LRI_HTTPHEADER) source_list = handle->httpheader; if (!source_list) { *strlist = NULL; break; } *strlist = lr_strv_dup(source_list); break; } case LRI_FETCHMIRRORS: lnum = va_arg(arg, long *); *lnum = (long) handle->fetchmirrors; break; case LRI_MAXMIRRORTRIES: lnum = va_arg(arg, long *); *lnum = (long) handle->maxmirrortries; break; case LRI_YUMSLIST: { LrUrlVars **slist = va_arg(arg, LrUrlVars **); *slist = handle->yumslist; break; } case LRI_VARSUB: { LrUrlVars **vars = va_arg(arg, LrUrlVars **); *vars = handle->urlvars; break; } case LRI_MIRRORS: { int x; char ***list = va_arg(arg, char ***); *list = NULL; LrInternalMirrorlist *ml = handle->mirrors; if (!ml) // lr_handle_perform() or lr_download_package() was not called yet break; /* Make list of urls from internal mirrorlist */ x = 0; *list = lr_malloc((g_slist_length(ml) + 1) * sizeof(char *)); for (LrInternalMirrorlist *elem = ml; elem; elem = g_slist_next(elem)) { LrInternalMirror *mirror = elem->data; (*list)[x] = g_strdup(mirror->url); x++; } (*list)[x] = NULL; break; } case LRI_METALINK: { LrMetalink **metalink = va_arg(arg, LrMetalink **); *metalink = handle->metalink; break; } case LRI_FASTESTMIRROR: lnum = va_arg(arg, long *); *lnum = (long) handle->fastestmirror; break; case LRI_FASTESTMIRRORCACHE: str = va_arg(arg, char **); *str = handle->fastestmirrorcache; break; case LRI_FASTESTMIRRORMAXAGE: lnum = va_arg(arg, long *); *lnum = (long) handle->fastestmirrormaxage; break; case LRI_HMFCB: { LrHandleMirrorFailureCb *cb= va_arg(arg, LrHandleMirrorFailureCb *); *cb = handle->hmfcb; break; } case LRI_SSLVERIFYPEER: lnum = va_arg(arg, long *); *lnum = (long) handle->sslverifypeer; break; case LRI_SSLVERIFYHOST: lnum = va_arg(arg, long *); *lnum = (long) (handle->sslverifyhost ? 1 : 0); break; case LRI_SSLCLIENTCERT: str = va_arg(arg, char **); *str = handle->sslclientcert; break; case LRI_SSLCLIENTKEY: str = va_arg(arg, char **); *str = handle->sslclientkey; break; case LRI_SSLCACERT: str = va_arg(arg, char **); *str = handle->sslcacert; break; case LRI_IPRESOLVE: { LrIpResolveType *type = va_arg(arg, LrIpResolveType *); *type = (LrIpResolveType) (handle->ipresolve); break; } case LRI_ALLOWEDMIRRORFAILURES: lnum = va_arg(arg, long *); *lnum = (long) (handle->allowed_mirror_failures); break; case LRI_ADAPTIVEMIRRORSORTING: lnum = va_arg(arg, long *); *lnum = (long) (handle->adaptivemirrorsorting); break; case LRI_GNUPGHOMEDIR: str = va_arg(arg, char **); *str = handle->gnupghomedir; break; case LRI_FASTESTMIRRORTIMEOUT: dnum = va_arg(arg, double *); *dnum = (double) handle->fastestmirrortimeout; break; case LRI_OFFLINE: lnum = va_arg(arg, long *); *lnum = (long) handle->offline; break; case LRI_LOWSPEEDTIME: lnum = va_arg(arg, long *); *lnum = (long) (handle->lowspeedtime); break; case LRI_LOWSPEEDLIMIT: lnum = va_arg(arg, long *); *lnum = (long) (handle->lowspeedlimit); break; case LRI_HTTPAUTHMETHODS: { LrAuth *auth = va_arg(arg, LrAuth *); *auth = handle->httpauthmethods; break; } case LRI_PROXYAUTHMETHODS: { LrAuth *auth = va_arg(arg, LrAuth *); *auth = handle->proxyauthmethods; break; } case LRI_FTPUSEEPSV: lnum = va_arg(arg, long *); *lnum = (long) handle->ftpuseepsv; break; case LRI_CACHEDIR: str = va_arg(arg, char **); *str = handle->cachedir; break; default: rc = FALSE; g_set_error(err, LR_HANDLE_ERROR, LRE_UNKNOWNOPT, "Unknown option"); break; } va_end(arg); return rc; } librepo-1.12.1/librepo/handle.h000066400000000000000000000466471372027637700163270ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_HANDLE_H__ #define __LR_HANDLE_H__ #include #include "result.h" G_BEGIN_DECLS /** \defgroup handle Librepo Handle * \addtogroup handle * @{ */ /** Handle object containing configuration for repository metadata and * package downloading. */ typedef struct _LrHandle LrHandle; #ifdef WITH_ZCHUNK /** Define LRO_SUPPORTS_CACHEDIR so clients can check for this feature at build * time */ #define LRO_SUPPORTS_CACHEDIR #endif /* WITH_ZCHUNK */ /** LRO_FASTESTMIRRORMAXAGE default value */ #define LRO_FASTESTMIRRORMAXAGE_DEFAULT 2592000L // 30 days /** LRO_FASTESTMIRRORMAXAGE minimal allowed value */ #define LRO_FASTESTMIRRORMAXAGE_MIN 0L /** LRO_PROXYPORT default value */ #define LRO_PROXYPORT_DEFAULT 1080L /** LRO_PROXYTYPE default value */ #define LRO_PROXYTYPE_DEFAULT LR_PROXY_HTTP /** LRO_MAXSPEED default value (0 == unlimited speed) */ #define LRO_MAXSPEED_DEFAULT G_GINT64_CONSTANT(0) /** LRO_CONNECTTIMEOUT default value */ #define LRO_CONNECTTIMEOUT_DEFAULT 30L /** LRO_MAXMIRRORTRIES default value */ #define LRO_MAXMIRRORTRIES_DEFAULT 0L /** LRO_MAXMIRRORTRIES minimal allowed value */ #define LRO_MAXMIRRORTRIES_MIN 0L /** LRO_MAXPARALLELDOWNLOADS default value */ #define LRO_MAXPARALLELDOWNLOADS_DEFAULT 3L /** LRO_MAXPARALLELDOWNLOADS minimal allowed value */ #define LRO_MAXPARALLELDOWNLOADS_MIN 1L /** LRO_MAXPARALLELDOWNLOADS maximal allowed value */ #define LRO_MAXPARALLELDOWNLOADS_MAX 20L /** LRO_MAXDOWNLOADSPERMIRROR default value */ #define LRO_MAXDOWNLOADSPERMIRROR_DEFAULT 3L /** LRO_MAXDOWNLOADSPERMIRROR minimal allowed value */ #define LRO_MAXDOWNLOADSPERMIRROR_MIN 1L /** LRO_LOWSPEEDTIME minimal allowed value */ #define LRO_LOWSPEEDTIME_MIN 0L /** LRO_LOWSPEEDTIME default value */ #define LRO_LOWSPEEDTIME_DEFAULT 30L /** LRO_LOWSPEEDLIMIT minimal allowed value */ #define LRO_LOWSPEEDLIMIT_MIN 0L /** LRO_LOWSPEEDLIMIT default value */ #define LRO_LOWSPEEDLIMIT_DEFAULT 1000L /** LRO_IPRESOLVE default value */ #define LRO_IPRESOLVE_DEFAULT LR_IPRESOLVE_WHATEVER /** LRO_ALLOWEDMIRRORFAILURES default value */ #define LRO_ALLOWEDMIRRORFAILURES_DEFAULT 4L /** LRO_ADAPTIVEMIRRORSORTING default value */ #define LRO_ADAPTIVEMIRRORSORTING_DEFAULT 1L /** LRO_GNUPGHOMEDIR default value */ #define LRO_GNUPGHOMEDIR_DEFAULT NULL /** LRO_FASTESTMIRRORTIMEOUT default value */ #define LRO_FASTESTMIRRORTIMEOUT_DEFAULT 2.0 /** LRO_OFFLINE default value */ #define LRO_OFFLINE_DEFAULT 0L /** LRO_HTTPAUTHMETHODS default value*/ #define LRO_HTTPAUTHMETHODS_DEFAULT LR_AUTH_BASIC /** LRO_PROXYAUTHMETHODS default value*/ #define LRO_PROXYAUTHMETHODS_DEFAULT LR_AUTH_BASIC /** LRO_FTPUSEEPSV default value */ #define LRO_FTPUSEEPSV_DEFAULT 1L /** Handle options for the ::lr_handle_setopt function. */ typedef enum { LRO_UPDATE, /*!< (long 1 or 0) Update existing repo in ::LrResult. Update means download missing (previously omitted) metadata file(s). */ LRO_URLS, /*!< (char ** NULL-terminated) List of base repo URLs */ LRO_MIRRORLIST, /*!< (char *) Mirrorlist or metalink url. This option is DEPRECATED! Use LRO_MIRRORLISTURL or LRO_METALINKURL instead. */ LRO_MIRRORLISTURL, /*!< (char *) Mirrorlist url */ LRO_METALINKURL, /*!< (char *) Metalink url */ LRO_LOCAL, /*!< (long 1 or 0) Do not duplicate local metadata, just locate the old one */ LRO_HTTPAUTH, /*!< (long 1 or 0) Enable all supported method of HTTP authentification. This option is DEPRECATED! Use LRO_HTTPAUTHMETHODS */ LRO_USERPWD, /*!< (char *) User and password for http authetification in format user:password */ LRO_PROXY, /*!< (char *) Address of proxy server eg. "proxy-host.com:8080" */ LRO_PROXYPORT, /*!< (long) Set port number for proxy separately. Default port is 1080. */ LRO_PROXYTYPE, /*!< (LrProxyType) Type of the proxy used. */ LRO_PROXYAUTH, /*!< (long 1 or 0) Enable all supported method for proxy authentification. This option is DEPRECATED! Use LRO_PROXYAUTHMETHODS */ LRO_PROXYUSERPWD, /*!< (char *) User and password for proxy in format user:password */ LRO_PROGRESSCB, /*!< (LrProgressCb) Progress callback */ LRO_PROGRESSDATA, /*!< (void *) Progress callback user data */ LRO_MAXSPEED, /*!< (gint64) Maximum download speed in bytes per second. Default is 0 = unlimited download speed. */ LRO_DESTDIR, /*!< (char *) Where to save downloaded files */ LRO_REPOTYPE, /*!< (LrRepotype) Type of downloaded repo, currently only supported is LR_YUMREPO. */ LRO_CONNECTTIMEOUT, /*!< (long) Max time in sec for connection phase. default timeout is 300 seconds. */ LRO_IGNOREMISSING, /*!< (long 1 or 0) If you want to localise (LRO_LOCAL is enabled) a incomplete local repository (eg. only primary and filelists are present) you could use LRO_YUMDLIST and specify only file that are present, or use this option. */ LRO_INTERRUPTIBLE, /*!< (long 1 or 0) If true, Librepo setups its own signal handler for SIGTERM and stops downloading if SIGTERM is catched. In this case current operation could return any kind of error code. Handle which operation was interrupted should never be used again! */ LRO_USERAGENT, /*!< (char *) String for User-Agent: header in the http request sent to the remote server */ LRO_FETCHMIRRORS, /*!< (long 1 or 0) If true - do not download anything, except mirrorlist or metalink (during lr_handle_perform()).*/ LRO_MAXMIRRORTRIES, /*!< (long) If download fails try at most the specified number of mirrors. 0 means try all available mirrors. */ LRO_MAXPARALLELDOWNLOADS, /*!< (long) Maximum number of parallel downloads. */ LRO_MAXDOWNLOADSPERMIRROR, /*!< (long) Maximum number of parallel downloads per mirror. */ LRO_VARSUB, /*!< (LrUrlVars *) Variables and its substitutions for repo URL. [{"releasever", "f18"}], ...; (e.g.: http://foo/$releasever => http://foo/f18) LrUrlVars has to be constructed by the ::lr_urlvars_set() function. After set the list to the handle, it has not to be freed! Handle itself takes care about freeing the list. */ LRO_FASTESTMIRROR, /*!< (long 1 or 0) Sort the internal mirrorlist, after it is constructed, by the determined connection speed. Disabled by default. */ LRO_FASTESTMIRRORCACHE, /*!< (char *) Path to the fastestmirror's cache file. Used when LRO_FASTESTMIRROR is enabled. If it doesn't exists, it will be created. */ LRO_FASTESTMIRRORMAXAGE, /*< (long) Maximum age of a record in cache (seconds). Default: 2592000 (30 days). */ LRO_FASTESTMIRRORCB, /* (LrFastestMirrorCb) Fastest mirror status callback */ LRO_FASTESTMIRRORDATA, /* (void *) User data for LRO_FASTESTMIRRORCB */ LRO_LOWSPEEDTIME, /*< (long) The time in seconds that the transfer should be below the LRO_LOWSPEEDLIMIT for the library to consider it too slow and abort. */ LRO_LOWSPEEDLIMIT, /*< (long) The transfer speed in bytes per second that the transfer should be below during LRO_LOWSPEEDTIME seconds for the library to consider it too slow and abort. */ /* Repo common options */ LRO_GPGCHECK, /*!< (long 1 or 0) Check GPG signature if available */ LRO_CHECKSUM, /*!< (long 1 or 0) Check files checksum if available */ /* LR_YUMREPO specific options */ LRO_YUMDLIST, /*!< (char ** NULL-terminated) Download only specified records from repomd (e.g. ["primary", "filelists", NULL]). Note: Last element of the list must be NULL! */ LRO_RPMMDDLIST = LRO_YUMDLIST, LRO_YUMBLIST, /*!< (char ** NULL-terminated) Do not download this specified records from repomd (blacklist). Note: Last element of the list must be NULL! */ LRO_RPMMDBLIST = LRO_YUMBLIST, LRO_HMFCB, /*!< (LrHandleMirrorFailureCb) Handle specific mirror failure callback. Callback called when a repodata download from a mirror fails. This callback gets the user data set by LRO_PROGRESSDATA */ LRO_SSLVERIFYPEER, /*!< (long 1 or 0) This option determines whether librepo verifies the authenticity of the peer's certificate. This trust is based on a chain of digital signatures, rooted in certification authority (CA) certificates. */ LRO_SSLVERIFYHOST, /*!< (long 1 or 0) This option determines whether librepo verifies that the server cert is for the server it is known as. */ LRO_IPRESOLVE, /*!< (LrIpResolveType) Sets what kind of IP addresses to use when resolving host names. */ LRO_ALLOWEDMIRRORFAILURES, /*!< (long) If all transfers from a mirror failed (no successful transfer from the mirror exists) and the number of failed downloads is higher or equal to this value the mirror will be skipped (ignored) for all next downloads. Note: Number of failed transfers for a single mirror can outreach this number! For example, if you set this value to 1 but you allow 3 parallel downloads it is possible that all three downloads start from the mirror, before any of them can fail. Then, if all three transfers fail, the number of failures for the mirror will be 3, even if this option was set to 1. Set -1 or 0 to disable this option */ LRO_ADAPTIVEMIRRORSORTING, /*!< (long 1 or 0) If enabled, internal list of mirrors for each handle is re-sorted after each finished transfer. The the sorting is based on mirror error rate etc. */ LRO_GNUPGHOMEDIR, /*!< (char *) Configuration directory for GNUPG (a directory with keyring) */ LRO_FASTESTMIRRORTIMEOUT, /*!< (double) Max length of fastest mirror measurement in seconds. Default value is 2sec */ LRO_HTTPHEADER, /*!< (char ** NULL-terminated) List of HTTP header to pass to the server and/or proxy in Librepo's HTTP requests. */ LRO_OFFLINE, /*!< (long 1 or 0) Make the handle work only locally, all remote URLs are ignored. Remote mirrorlists/metalinks (if they are specified) are ignored. Fastest mirror check (if enabled) is skipped. */ LRO_SSLCLIENTCERT, /*!< (char *) Path to the PEM format SSL client certificate librepo should use when talking to the server. */ LRO_SSLCLIENTKEY, /*!< (char *) Path to the PEM format SSL client key librepo should use when talking to the server, if not included in the client certificate file. */ LRO_SSLCACERT, /*!< (char *) Path to a file containing the list of PEM format trusted CA certificates. */ LRO_HTTPAUTHMETHODS, /*!< (LrAuth) Bitmask which tell Librepo which auth methods you wan to use. */ LRO_PROXYAUTHMETHODS, /*!< (LrAuth) A long bitmask which tell Librepo which auth methods you want to use for proxy auth. */ LRO_FTPUSEEPSV, /*!< (long 1 or 0) Enable/Disable EPSV (Extended Passive mode) for FTP. */ LRO_YUMSLIST, /*!< (LrUrlVars *) Repomd records and their substitutions. [{"group_gz", "group"}], ...; If var record is not listed in repomd, librepo will download val instead. After set the list to the handle, it has not to be freed! Handle itself takes care about freeing the list. */ LRO_CACHEDIR, /*!< (char *) Path to base cache directory for repositories, used by zchunk to find old files to copy data from. If this is NULL, zchunk will be disabled */ LRO_PRESERVETIME, /*!< (long 1 or 0) If enabled, librepo will try to keep timestamps of the downloaded files in sync with that on the remote side. */ LRO_ONETIMEFLAG, /*!< (char *) A one-time flag is a URL query parameter (in the form "key=value") that is added to the first HTTP request for LRO_METALINKURL or LRO_MIRRORLIST (whichever comes first) made with this handle. Any subsequent requests with this handle, including any retry attempts made by librepo internally, will not get the flag. This is useful for implementing a mechanism for counting the users of a repository (and, by extension, of the OS as a whole) in which all flags received by the server in a specific time period are summed up to produce a good estimate, with the advantage of bundling the flag into regular metadata updates and not sending separate requests just for that purpose alone. The guarantee is that, once set on this handle, the flag will only be used once and then thrown away (set to NULL). The program may choose to implement a sliding time window used to set this option on a regular basis (such as once per week), to allow for gathering usage statistics over time. For a reference implementation, see the "countme" option in dnf.conf(5) or the associated Fedora change: https://fedoraproject.org/wiki/Changes/DNF_Better_Counting */ LRO_SENTINEL, /*!< Sentinel */ } LrHandleOption; /*!< Handle config options */ /** Handle options for the ::lr_handle_getinfo function. */ typedef enum { LRI_UPDATE, /*!< (long *) */ LRI_URLS, /*!< (char *** Malloced) NOTE: Returned list must be freed as well as all its items! You could use g_strfreev() function. */ LRI_MIRRORLIST, /*!< (char **) */ LRI_MIRRORLISTURL, /*!< (char **) */ LRI_METALINKURL, /*!< (char **) */ LRI_LOCAL, /*!< (long *) */ LRI_PROGRESSCB, /*!< (void *) */ LRI_PROGRESSDATA, /*!< (LrProgressCb) */ LRI_DESTDIR, /*!< (char **) */ LRI_REPOTYPE, /*!< (long *) */ LRI_USERAGENT, /*!< (char **) */ LRI_YUMDLIST, /*!< (char *** Malloced) NOTE: Returned list must be freed as well as all its items! You could use g_strfreev() function. */ LRI_RPMMDDLIST = LRI_YUMDLIST, LRI_YUMBLIST, /*!< (char *** Malloced) NOTE: Returned list must be freed as well as all its items! You could use g_strfreev() function. */ LRI_RPMMDBLIST = LRI_YUMBLIST, LRI_FETCHMIRRORS, /*!< (long *) */ LRI_MAXMIRRORTRIES, /*!< (long *) */ LRI_VARSUB, /*!< (LrUrlVars **) */ LRI_MIRRORS, /*!< (char *** Malloced) Mirrorlist associated with the repository. Mirrors on this list are mirrors parsed from mirrorlist/metalink specified by LRO_MIRRORLIST or from mirrorlist specified by LRO_MIRROSLISTURL and metalink specified by LRO_METALINKURL. No URLs specified by LRO_URLS are included in this list. NOTE: Returned list must be freed as well as all its items! You could use g_strfreev() function. */ LRI_METALINK, /*!< (LrMetalink *) */ LRI_FASTESTMIRROR, /*!< (long *) */ LRI_FASTESTMIRRORCACHE, /*!< (char **) */ LRI_FASTESTMIRRORMAXAGE, /*!< (long *) */ LRI_HMFCB, /*!< (LrHandleMirrorFailureCb) */ LRI_SSLVERIFYPEER, /*!< (long *) */ LRI_SSLVERIFYHOST, /*!< (long *) */ LRI_IPRESOLVE, /*!< (LrIpResolveType *) */ LRI_ALLOWEDMIRRORFAILURES, /*!< (long *) */ LRI_ADAPTIVEMIRRORSORTING, /*!< (long *) */ LRI_GNUPGHOMEDIR, /*!< (char **) */ LRI_FASTESTMIRRORTIMEOUT, /*!< (double *) */ LRI_HTTPHEADER, /*!< (char *** Malloced) NOTE: Returned list must be freed as well as all its items! You could use g_strfreev() function. */ LRI_OFFLINE, /*!< (long *) */ LRI_SSLCLIENTCERT, /*!< (char **) */ LRI_SSLCLIENTKEY, /*!< (char **) */ LRI_SSLCACERT, /*!< (char **) */ LRI_LOWSPEEDTIME, /*!< (long) */ LRI_LOWSPEEDLIMIT, /*!< (long) */ LRI_HTTPAUTHMETHODS, /*!< (LrAuth) */ LRI_PROXYAUTHMETHODS, /*!< (LrAuth) */ LRI_FTPUSEEPSV, /*!< (long) */ LRI_YUMSLIST, /*!< (LrUrlVars **) */ LRI_CACHEDIR, /*!< (char *) */ LRI_SENTINEL, } LrHandleInfoOption; /*!< Handle info options */ /** Return new handle. * @return New allocated handle. */ LrHandle * lr_handle_init(void); /** Frees handle and its content. * @param handle Handle. */ void lr_handle_free(LrHandle *handle); /** Set option (::LrHandleOption) of the handle. * @param handle Handle. * @param err GError ** * @param option Option from ::LrHandleOption enum. * @param ... Value for the option. * @return TRUE if everything is ok, FALSE if err is set. */ gboolean lr_handle_setopt(LrHandle *handle, GError **err, LrHandleOption option, ...); /** Get information from handle. * Most of returned pointers point directly to the handle internal * values and therefore you should assume that they are only valid until * any manipulation (lr_handle_setopt, lr_handle_perform, ...) * with handle occurs. * NOTE: You should not free or modify the memory returned by this * function unless it is explicitly mentioned! * @param handle Librepo handle. * @param err GError ** * @param option Option from ::LrHandleInfoOption enum. * @param ... Appropriate variable for the selected option. * @return TRUE if everything is ok, FALSE if err is set. */ gboolean lr_handle_getinfo(LrHandle *handle, GError **err, LrHandleInfoOption option, ...); /** Perform repodata download or location. * @param handle Librepo handle. * @param result Librepo result. * @param err GError ** * @return TRUE if everything is ok, FALSE if err is set. */ gboolean lr_handle_perform(LrHandle *handle, LrResult *result, GError **err); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/handle_internal.h000066400000000000000000000157641372027637700202170ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_HANDLE_INTERNAL_H__ #define __LR_HANDLE_INTERNAL_H__ #include #include #include "types.h" #include "handle.h" #include "lrmirrorlist.h" #include "url_substitution.h" G_BEGIN_DECLS #define TMP_DIR_TEMPLATE "librepo-XXXXXX" struct _LrHandle { CURL *curl_handle; /*!< CURL handle */ int update; /*!< Just update existing repo */ LrInternalMirrorlist *internal_mirrorlist; /*!< Internal list of mirrors (Real used mirrorlist = baseurl + mirrors) */ char **urls; /*!< URLs of repositories */ LrInternalMirrorlist *urls_mirrors; /*!< Mirrors from urls */ int fastestmirror; /*!< Should be internal mirrorlist sorted by connection time */ char *fastestmirrorcache; /*!< Path to the fastestmirror's cache file. */ long fastestmirrormaxage; /*!< Maximum age of a record in cache (seconds). */ LrFastestMirrorCb fastestmirrorcb; /*!< Fastest mirror detection status callback */ void *fastestmirrordata; /*!< User data for fastestmirrorcb. */ // Mirrorlist related stuff char *mirrorlist; /*!< XXX: Deprecated! List of or metalink */ char *mirrorlisturl; /*!< Mirrorlist URL */ int mirrorlist_fd; /*!< Raw downloaded mirrorlist file */ LrInternalMirrorlist *mirrorlist_mirrors; /*!< Mirrors from mirrorlist */ // Metalink related stuff char * metalinkurl; /*!< Metalink URL */ int metalink_fd; /*!< Raw downloaded metalink file */ LrInternalMirrorlist *metalink_mirrors; /*!< Mirrors from metalink */ LrMetalink *metalink; /*!< Parsed metalink for repomd.xml */ LrInternalMirrorlist *mirrors; /*!< Mirrors from metalink or mirrorlist */ char *onetimeflag; /*!< See: LRO_ONETIMEFLAG */ gboolean onetimeflag_apply; /*!< Whether LRO_ONETIMEFLAG should be applied to the next CURL request */ int local; /*!< Do not duplicate local data */ char *used_mirror; /*!< Finally used mirror (if any) */ char *destdir; /*!< Destination directory */ char *useragent; /*!< User agent */ LrRepotype repotype; /*!< Type of repository */ LrChecks checks; /*!< Which check should be applied */ LrProgressCb user_cb; /*!< User progress callback */ void *user_data; /*!< User data for callback */ int ignoremissing; /*!< Ignore missing metadata files */ int interruptible; /*!< Setup own SIGTERM handler*/ char **yumdlist; /*!< Repomd data typenames to download NULL - Download all yumdlist[0] = NULL - Only repomd.xml */ char **yumblist; /*!< Repomd data typenames to skip (blacklist). NULL as argument will disable blacklist. */ int fetchmirrors; /*!< Only fetch and parse mirrorlist. */ int maxmirrortries; /*!< Try at most this number of mirrors. */ long maxparalleldownloads; /* !< Maximum number of parallel downloads. */ long maxdownloadspermirror; /* !< Maximum number of parallel downloads per a single mirror. */ LrUrlVars *urlvars; /*!< List with url substitutions */ long lowspeedtime; /*!< The time in seconds that the transfer should be below the LRO_LOWSPEEDLIMIT for the library to consider it too slow and abort. */ long lowspeedlimit; /*!< The transfer speed in bytes per second that the transfer should be below during LRO_LOWSPEEDTIME seconds for the library to consider it too slow and abort. */ LrHandleMirrorFailureCb hmfcb; /*!< Callback called when a repodata download from a mirror fails. */ gint64 maxspeed; /*!< Max speed in bytes per sec */ long sslverifypeer; /*!< Determines whether verify the authenticity of the peer's certificate */ long sslverifyhost; /*!< Determines whether the server name should be checked against the name in the certificate */ char *sslclientcert; /*!< Client certificate filename. */ char *sslclientkey; /*!< Client certificate key. */ char *sslcacert; /*!< CA certificate path. */ LrIpResolveType ipresolve; /*!< What kind of IP addresses to use when resolving host names. */ long allowed_mirror_failures; /*!< Number of allowed failed transfers, when there are no successful ones, before a mirror gets ignored. */ long adaptivemirrorsorting; /*!< See: LRO_ADAPTIVEMIRRORSORTING */ gchar *gnupghomedir; /*!< GNUPG home dir. */ gdouble fastestmirrortimeout; /*!< Max length of fastest mirror measurement in seconds. */ gchar **httpheader; /*!< List of HTTP headers. Curl doesn't copy HTTP header values from curl_slist. We need to keep them around. */ gboolean offline; /*!< If TRUE, librepo should work offline - ignore all non local URLs, etc. */ LrAuth httpauthmethods; /*!< Bitmask with auth methods */ LrAuth proxyauthmethods; /*!< Bitmask with auth methods */ long ftpuseepsv; /*!< Use FTP EPSV (extended passive mode) mode */ gchar *cachedir; /*!< Base cache dir for repositories */ long preservetime; /*!< Preserve timestamps of downloaded files */ LrUrlVars *yumslist; }; /** Return new CURL easy handle with some default options setted. */ CURL * lr_get_curl_handle(); /** * Create (if do not exists) internal mirrorlist. Insert baseurl (if * specified) and download, parse and insert mirrors from mirrorlist url. * @param handle Librepo handle. * @param usefastestmirror Sort internal mirrorlist by speed of mirrors * @param err GError ** * @return returns TRUE if error is not set and FALSE if it is. */ gboolean lr_handle_prepare_internal_mirrorlist(LrHandle *handle, gboolean usefastestmirror, GError **err); G_END_DECLS #endif librepo-1.12.1/librepo/librepo.h000066400000000000000000000030401372027637700165040ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_LIBREPO_H__ #define __LR_LIBREPO_H__ #include #include "checksum.h" #include "fastestmirror.h" #include "gpg.h" #include "handle.h" #include "metalink.h" #include "metadata_downloader.h" #include "package_downloader.h" #include "rcodes.h" #include "repoconf.h" #include "repomd.h" #include "repoutil_yum.h" #include "result.h" #include "types.h" #include "url_substitution.h" #include "util.h" #include "version.h" #include "xmlparser.h" #include "yum.h" // Low level downloading interface // (API could be changed significantly between two versions) #include "downloader.h" #include "downloadtarget.h" #endif librepo-1.12.1/librepo/librepo.pc.cmake000066400000000000000000000005431372027637700177430ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: librepo Description: Repodata downloading library. Version: @VERSION@ Requires: glib-2.0 Requires.private: libcurl openssl libxml-2.0 Libs: -L${libdir} -lrepo Libs.private: -lgpgme -lgpg-error Cflags: -I${includedir} -D_FILE_OFFSET_BITS=64 librepo-1.12.1/librepo/lrmirrorlist.c000066400000000000000000000130211372027637700176070ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "util.h" #include "lrmirrorlist.h" LrProtocol lr_detect_protocol(const char *url) { assert(url); if (g_str_has_prefix(url, "http://") || g_str_has_prefix(url, "https://")) return LR_PROTOCOL_HTTP; if (g_str_has_prefix(url, "ftp://")) return LR_PROTOCOL_FTP; if (g_str_has_prefix(url, "file:/")) return LR_PROTOCOL_FILE; if (g_str_has_prefix(url, "rsync://")) return LR_PROTOCOL_RSYNC; return LR_PROTOCOL_OTHER; } static LrInternalMirror * lr_lrmirror_new(const char *url, LrUrlVars *urlvars) { LrInternalMirror *mirror; mirror = lr_malloc0(sizeof(*mirror)); mirror->url = lr_url_substitute(url, urlvars); return mirror; } static void lr_lrmirror_free(void *data) { LrInternalMirror *mirror = data; lr_free(mirror->url); lr_free(mirror); } void lr_lrmirrorlist_free(LrInternalMirrorlist *list) { if (!list) return; g_slist_free_full(list, lr_lrmirror_free); } LrInternalMirrorlist * lr_lrmirrorlist_append_url(LrInternalMirrorlist *list, const char *url, LrUrlVars *urlvars) { if (!url || !strlen(url)) return list; LrInternalMirror *mirror = lr_lrmirror_new(url, urlvars); mirror->preference = 100; mirror->protocol = lr_detect_protocol(mirror->url); //g_debug("%s: Appending URL: %s", __func__, mirror->url); return g_slist_append(list, mirror); } LrInternalMirrorlist * lr_lrmirrorlist_append_mirrorlist(LrInternalMirrorlist *list, LrMirrorlist *mirrorlist, LrUrlVars *urlvars) { if (!mirrorlist || !mirrorlist->urls) return list; for (GSList *elem = mirrorlist->urls; elem; elem = g_slist_next(elem)) { char *url = elem->data; if (!url || !strlen(url)) continue; LrInternalMirror *mirror = lr_lrmirror_new(url, urlvars); mirror->preference = 100; mirror->protocol = lr_detect_protocol(mirror->url); list = g_slist_append(list, mirror); //g_debug("%s: Appending URL: %s", __func__, mirror->url); } return list; } LrInternalMirrorlist * lr_lrmirrorlist_append_metalink(LrInternalMirrorlist *list, LrMetalink *metalink, const char *suffix, LrUrlVars *urlvars) { size_t suffix_len = 0; if (!metalink || !metalink->urls) return list; if (suffix) suffix_len = strlen(suffix); for (GSList *elem = metalink->urls; elem; elem = g_slist_next(elem)) { LrMetalinkUrl *metalinkurl = elem->data; assert(metalinkurl); char *url = metalinkurl->url; if (!url) continue; // No url present size_t url_len = strlen(url); if (!url_len) continue; // No url present char *url_copy = NULL; if (suffix_len) { /* Remove suffix if necessary */ if (url_len >= suffix_len && !strcmp(url+(url_len-suffix_len), suffix)) url_copy = g_strndup(url, url_len-suffix_len); } if (!url_copy) url_copy = g_strdup(url); LrInternalMirror *mirror = lr_lrmirror_new(url_copy, urlvars); mirror->preference = metalinkurl->preference; mirror->protocol = lr_detect_protocol(mirror->url); lr_free(url_copy); list = g_slist_append(list, mirror); //g_debug("%s: Appending URL: %s", __func__, mirror->url); } return list; } LrInternalMirrorlist * lr_lrmirrorlist_append_lrmirrorlist(LrInternalMirrorlist *list, LrInternalMirrorlist *other) { if (!other) return list; for (LrInternalMirrorlist *elem = other; elem; elem = g_slist_next(elem)) { LrInternalMirror *oth = elem->data; if (!oth->url || !strlen(oth->url)) continue; LrInternalMirror *mirror = lr_lrmirror_new(oth->url, NULL); mirror->preference = oth->preference; mirror->protocol = oth->protocol; list = g_slist_append(list, mirror); //g_debug("%s: Appending URL: %s", __func__, mirror->url); } return list; } LrInternalMirror * lr_lrmirrorlist_nth(LrInternalMirrorlist *list, unsigned int nth) { return g_slist_nth_data(list, nth); } char * lr_lrmirrorlist_nth_url(LrInternalMirrorlist *list, unsigned int nth) { LrInternalMirror *mirror = g_slist_nth_data(list, nth); return (mirror) ? mirror->url : NULL; } librepo-1.12.1/librepo/lrmirrorlist.h000066400000000000000000000102311372027637700176140ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_LRMIRRORLIST_H__ #define __LR_LRMIRRORLIST_H__ #include #include "url_substitution.h" #include "mirrorlist.h" #include "metalink.h" G_BEGIN_DECLS typedef enum { LR_PROTOCOL_OTHER, LR_PROTOCOL_FILE, LR_PROTOCOL_HTTP, LR_PROTOCOL_FTP, LR_PROTOCOL_RSYNC, } LrProtocol; /** A internal representation of a mirror */ typedef struct { char *url; /*!< URL of the mirror */ int preference; /*!< Integer number 1-100 - higher is better */ LrProtocol protocol; /*!< Protocol of this mirror */ } LrInternalMirror; typedef GSList LrInternalMirrorlist; /** Detect URL protocol. * @param url URL * @return Type of detected protocol */ LrProtocol lr_detect_protocol(const char *url); /** Append url to the mirrorlist. * @param list a LrInternalMirrorlist or NULL * @param url the Url * @param urlvars a LrUrlVars or NULL * @return the new start of the LrInternalMirrorlist */ LrInternalMirrorlist * lr_lrmirrorlist_append_url(LrInternalMirrorlist *list, const char *url, LrUrlVars *urlvars); /** Append mirrors from mirrorlist to the internal mirrorlist. * @param iml Internal mirrorlist or NULL * @param mirrorlist Mirrorlist * @param urlvars a LrUrlVars or NULL * @return the new start of the LrInternalMirrorlist */ LrInternalMirrorlist * lr_lrmirrorlist_append_mirrorlist(LrInternalMirrorlist *list, LrMirrorlist *mirrorlist, LrUrlVars *urlvars); /** Append mirrors from metalink to the internal mirrorlist. * @param iml Internal mirrorlist or NULL * @param metalink Metalink * @param suffix Suffix that shoud be removed from the metalink urls * @param urlvars a LrUrlVars or NULL * @return the new start of the LrInternalMirrorlist */ LrInternalMirrorlist * lr_lrmirrorlist_append_metalink(LrInternalMirrorlist *list, LrMetalink *metalink, const char *suffix, LrUrlVars *urlvars); /** Append mirrors from another LrInternalMirrorlist. * @param iml Internal mirrorlist * @param ml Other internal mirrorlist * @return the new start of the LrInternalMirrorlist */ LrInternalMirrorlist * lr_lrmirrorlist_append_lrmirrorlist(LrInternalMirrorlist *list, LrInternalMirrorlist *other); /** Return mirror on the given position. * @param list a LrInternalMirrorlist * @param nth the position of the mirror * @return the mirror */ LrInternalMirror * lr_lrmirrorlist_nth(LrInternalMirrorlist *list, unsigned int nth); /** Return url of the mirror on at the given position. * @param list a LrInternalMirrorlist * @param nth the position of the mirror * @return the url */ char * lr_lrmirrorlist_nth_url(LrInternalMirrorlist *list, unsigned int nth); /** Free LrInternalMirrorlist. * @param list Internal mirrorlist */ void lr_lrmirrorlist_free(LrInternalMirrorlist *list); G_END_DECLS #endif librepo-1.12.1/librepo/metadata_downloader.c000066400000000000000000000321611372027637700210470ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2016 Martin Hatina * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #define RESERVE 128 #include #include #include #include #include #include "librepo/librepo.h" #include "handle_internal.h" #include "librepo.h" LrMetadataTarget * lr_metadatatarget_new(LrHandle *handle, LrYumRepo *repo, LrYumRepoMd *repomd, void *cbdata, GError **err) { LrMetadataTarget *target; assert(!err || *err == NULL); target = lr_malloc0(sizeof(*target)); if (!target) { g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_MEMORY, "Out of memory"); return NULL; } target->chunk = g_string_chunk_new(16); target->handle = handle; target->repo = repo; target->repomd = repomd; target->cbdata = cbdata; target->repomd_records_to_download = 0; target->repomd_records_downloaded = 0; target->download_target = NULL; target->gnupghomedir = NULL; target->err = NULL; return target; } LrMetadataTarget * lr_metadatatarget_new2(LrHandle *handle, void *cbdata, LrProgressCb progresscb, LrMirrorFailureCb mirrorfailure_cb, LrEndCb endcb, const char *gnupghomedir, GError **err) { LrMetadataTarget *target = lr_metadatatarget_new(handle, NULL, NULL, cbdata, err); target->progresscb = progresscb; target->mirrorfailurecb = mirrorfailure_cb; target->endcb = endcb; target->gnupghomedir = g_string_chunk_insert(target->chunk, gnupghomedir); return target; } void lr_metadatatarget_free(LrMetadataTarget *target) { if (!target) return; g_string_chunk_free(target->chunk); if (target->err != NULL) g_list_free(target->err); g_free(target); } void lr_metadatatarget_append_error(LrMetadataTarget *target, char *format, ...) { va_list valist; size_t length = strlen(format); char *error_message = NULL; va_start(valist, format); while (1) { char *arg = va_arg(valist, char*); if (arg == NULL) break; length += strlen(arg); } length += RESERVE; va_end(valist); va_start(valist, format); error_message = malloc(length * sizeof(char)); vsnprintf(error_message, length, format, valist); va_end(valist); target->err = g_list_append(target->err, (gpointer) error_message); } static gboolean lr_setup_sigaction(struct sigaction *old_sigact, gboolean interruptible, GError **err) { if (interruptible) { struct sigaction sigact; g_debug("%s: Using own SIGINT handler", __func__); memset(&sigact, 0, sizeof(sigact)); sigemptyset(&sigact.sa_mask); sigact.sa_handler = lr_sigint_handler; sigaddset(&sigact.sa_mask, SIGINT); sigact.sa_flags = SA_RESTART; if (sigaction(SIGINT, &sigact, old_sigact) == -1) { g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_SIGACTION, "Cannot set Librepo SIGINT handler"); return FALSE; } } return TRUE; } static gboolean lr_restore_sigaction(struct sigaction *old_sigact, gboolean interruptible, GError **err) { // Restore original signal handler if (interruptible) { g_debug("%s: Restoring an old SIGINT handler", __func__); sigaction(SIGINT, old_sigact, NULL); if (lr_interrupt) { if (err && *err != NULL) g_clear_error(err); g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_INTERRUPTED, "Interrupted by a SIGINT signal"); return FALSE; } } return TRUE; } GSList * appendFdValue(GSList *fd_list, int fd) { int *fd_allocated = malloc(sizeof(int)); *fd_allocated = fd; fd_list = g_slist_append(fd_list, fd_allocated); return fd_list; } GSList * appendPath(GSList *paths, const char *path) { paths = g_slist_append(paths, g_strdup(path)); return paths; } void fillInvalidationValues(GSList **fd_list, GSList **paths) { (*fd_list) = appendFdValue((*fd_list), -1); (*paths) = appendPath((*paths), ""); } void handle_failure(LrMetadataTarget *target, GSList **fd_list, GSList **paths, GError *err) { lr_metadatatarget_append_error(target, err->message, NULL); fillInvalidationValues(fd_list, paths); g_error_free(err); } void create_repomd_xml_download_targets(GSList *targets, GSList **download_targets, GSList **fd_list, GSList **paths) { for (GSList *elem = targets; elem; elem = g_slist_next(elem)) { LrMetadataTarget *target = elem->data; LrDownloadTarget *download_target; GSList *checksums = NULL; GError *err = NULL; LrHandle *handle; char *path = NULL; int fd = -1; if (!target->handle) { /* every target must have fd and path, even invalid */ fillInvalidationValues(fd_list, paths); continue; } handle = target->handle; if (!handle->urls && !handle->mirrorlisturl && !handle->metalinkurl) { lr_metadatatarget_append_error(target, "No LRO_URLS, LRO_MIRRORLISTURL nor LRO_METALINKURL specified", NULL); fillInvalidationValues(fd_list, paths); continue; } if (handle->repotype != LR_YUMREPO) { lr_metadatatarget_append_error(target, "Bad LRO_REPOTYPE specified", NULL); fillInvalidationValues(fd_list, paths); continue; } if (target->repo == NULL) { target->repo = lr_yum_repo_init(); } if (target->repomd == NULL) { target->repomd = lr_yum_repomd_init(); } if (!lr_handle_prepare_internal_mirrorlist(handle, handle->fastestmirror, &err)) { lr_metadatatarget_append_error(target, "Cannot prepare internal mirrorlist: %s", err->message, NULL); fillInvalidationValues(fd_list, paths); g_error_free(err); continue; } if (mkdir(handle->destdir, S_IRWXU) == -1 && errno != EEXIST) { lr_metadatatarget_append_error(target, "Cannot create tmpdir: %s %s", handle->destdir, g_strerror(errno), NULL); fillInvalidationValues(fd_list, paths); g_error_free(err); continue; } if (!lr_prepare_repodata_dir(handle, &err)) { handle_failure(target, fd_list, paths, err); continue; } if (!handle->update) { if (!lr_store_mirrorlist_files(handle, target->repo, &err)) { handle_failure(target, fd_list, paths, err); continue; } if (!lr_copy_metalink_content(handle, target->repo, &err)) { handle_failure(target, fd_list, paths, err); continue; } if ((fd = lr_prepare_repomd_xml_file(handle, &path, &err)) == -1) { handle_failure(target, fd_list, paths, err); continue; } } if (handle->metalink && (handle->checks & LR_CHECK_CHECKSUM)) { lr_get_best_checksum(handle->metalink, &checksums); } CbData *cbdata = lr_get_metadata_failure_callback(handle); download_target = lr_downloadtarget_new(target->handle, "repodata/repomd.xml", NULL, fd, NULL, checksums, 0, 0, NULL, cbdata, NULL, (cbdata) ? hmfcb : NULL, target, 0, 0, NULL, TRUE, FALSE); target->download_target = download_target; (*download_targets) = g_slist_append((*download_targets), download_target); (*fd_list) = appendFdValue((*fd_list), fd); (*paths) = appendPath((*paths), path); } } void process_repomd_xml(GSList *targets, GSList *fd_list, GSList *paths) { GError *error = NULL; for (GSList *elem = targets, *fd = fd_list, *path = paths; elem; elem = g_slist_next(elem), fd = g_slist_next(fd), path = g_slist_next(path)) { LrMetadataTarget *target = elem->data; LrHandle *handle; gboolean ret; int fd_value = *((int *) fd->data); if (!target->handle || fd_value == -1) { goto fail; } handle = target->handle; handle->used_mirror = g_strdup(target->download_target->usedmirror); handle->gnupghomedir = g_strdup(target->gnupghomedir); if (target->download_target->rcode != LRE_OK) { lr_metadatatarget_append_error(target, (char *) lr_strerror(target->download_target->rcode), NULL); goto fail; } if (!lr_check_repomd_xml_asc_availability(handle, target->repo, fd_value, path->data, &error)) { lr_metadatatarget_append_error(target, error->message, NULL); g_error_free(error); goto fail; } lseek(fd_value, SEEK_SET, 0); ret = lr_yum_repomd_parse_file(target->repomd, fd_value, lr_xml_parser_warning_logger, "Repomd xml parser", &error); if (!ret) { lr_metadatatarget_append_error(target, "Parsing unsuccessful: %s", error->message, NULL); g_error_free(error); goto fail; } close(fd_value); lr_free(fd->data); target->repo->destdir = g_strdup(handle->destdir); target->repo->repomd = path->data; continue; fail: if (fd_value != -1) { close(fd_value); } lr_free(path->data); lr_free(fd->data); } } static gboolean lr_metadata_download_cleanup(GSList *download_targets) { gboolean ret = TRUE; for (GSList *elem = download_targets; elem; elem = g_slist_next(elem)) { LrDownloadTarget *download_target = elem->data; LrMetadataTarget *target = download_target->userdata; if (download_target->err) lr_metadatatarget_append_error(target, download_target->err, NULL); if (target->err != NULL) { ret = FALSE; } lr_downloadtarget_free(download_target); } return ret; } gboolean cleanup(GSList *download_targets, GError **err) { struct sigaction old_sigact; GError *repo_error = NULL; lr_metadata_download_cleanup(download_targets); if (!lr_restore_sigaction(&old_sigact, FALSE, &repo_error)) { g_propagate_error(err, repo_error); return FALSE; } return *err == NULL; } gboolean lr_download_metadata(GSList *targets, GError **err) { struct sigaction old_sigact; GSList *download_targets = NULL; GSList *fd_list = NULL; GSList *paths = NULL; assert(!err || *err == NULL); if (!targets) return TRUE; if (!lr_setup_sigaction(&old_sigact, FALSE, err)) { return FALSE; } create_repomd_xml_download_targets(targets, &download_targets, &fd_list, &paths); if (!lr_download(download_targets, FALSE, err)) { return cleanup(download_targets, err); } process_repomd_xml(targets, fd_list, paths); g_slist_free(fd_list); g_slist_free(paths); lr_yum_download_repos(targets, err); return cleanup(download_targets, err); } librepo-1.12.1/librepo/metadata_downloader.h000066400000000000000000000073251372027637700210600ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2016 Martin Hatina * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef LIBREPO_METADATA_DOWNLOADER_H #define LIBREPO_METADATA_DOWNLOADER_H #include #include "yum.h" #include "handle.h" #include "repomd.h" #include "downloadtarget.h" /** LrMetadataTarget structure */ typedef struct { LrHandle *handle; /*!< Related handle */ LrYumRepo *repo; /*!< Related repo */ LrYumRepoMd *repomd; /*!< Related repomd */ int repomd_records_to_download; int repomd_records_downloaded;/*!< Necessary for endcb, which would be otherwise called prematurely */ void *cbdata; /*!< Callback data */ LrProgressCb progresscb; /*!< Progress callback */ LrMirrorFailureCb mirrorfailurecb; LrEndCb endcb; /*!< Callback called when target transfer is done. (Use status to check if successfully or unsuccessfully) */ GList *err; /*!< Array of error messages or NULL. NULL means no error. */ GStringChunk *chunk; /*!< String chunk */ LrDownloadTarget *download_target; /*!< Download target for repomd.xml */ gchar *gnupghomedir; /*!< GNUPG home dir. */ } LrMetadataTarget; /** * Create new LrMetadataTarget object. * @param handle Handle related to this download or NULL. * @param repo Related repo. * @param repomd Related repomd. * @param cbdata User data for the callback * @param err GError ** * @return Newly allocated LrMetadataTarget or NULL on error */ LrMetadataTarget * lr_metadatatarget_new(LrHandle *handle, LrYumRepo *repo, LrYumRepoMd *repomd, void *cbdata, GError **err); /** * Create new LrMetadataTarget object. * Almost same as lr_metadatatarget_new() except this function * could set more callbacks. * @param handle Handle related to this download or NULL. * @param cbdata User data for the callback * @param progresscb Progress callback for this transfer. * @param mirror_failure_cb Called when download from a mirror failed. * @param endcb Callback called when target transfer is done. * @param err GError ** * @return Newly allocated LrMetadataTarget or NULL on error */ LrMetadataTarget * lr_metadatatarget_new2(LrHandle *handle, void *cbdata, LrProgressCb progresscb, LrMirrorFailureCb mirror_failure_cb, LrEndCb endcb, const char *gnupghomedir, GError **err); /** Free ::LrMetadataTarget object. * @param target LrMetadataTarget object */ void lr_metadatatarget_free(LrMetadataTarget *target); /** * Download all LrMetadataTargets at the targets GSList. * @param targets GSList where each element is a ::LrPackageTarget object * @param err GError ** * @return If FALSE then err is set. */ gboolean lr_download_metadata(GSList *targets, GError **err); #endif //LIBREPO_METADATA_DOWNLOADER_H librepo-1.12.1/librepo/metalink.c000066400000000000000000000346621372027637700166650ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "rcodes.h" #include "util.h" #include "metalink.h" #include "xmlparser_internal.h" /** TODO: * - (?) Use GStringChunk */ #define CHUNK_SIZE 8192 #define CONTENT_REALLOC_STEP 256 /* Metalink object manipulation helpers */ static LrMetalinkHash * lr_new_metalinkhash(LrMetalink *m) { assert(m); LrMetalinkHash *hash = lr_malloc0(sizeof(*hash)); m->hashes = g_slist_append(m->hashes, hash); return hash; } static LrMetalinkHash * lr_new_metalinkalternate_hash(LrMetalinkAlternate *ma) { assert(ma); LrMetalinkHash *hash = lr_malloc0(sizeof(*hash)); ma->hashes = g_slist_append(ma->hashes, hash); return hash; } static LrMetalinkUrl * lr_new_metalinkurl(LrMetalink *m) { assert(m); LrMetalinkUrl *url = lr_malloc0(sizeof(*url)); m->urls = g_slist_append(m->urls, url); return url; } static LrMetalinkAlternate * lr_new_metalinkalternate(LrMetalink *m) { assert(m); LrMetalinkAlternate *alternate = lr_malloc0(sizeof(*alternate)); m->alternates = g_slist_append(m->alternates, alternate); return alternate; } static void lr_free_metalinkhash(LrMetalinkHash *metalinkhash) { if (!metalinkhash) return; lr_free(metalinkhash->type); lr_free(metalinkhash->value); lr_free(metalinkhash); } static void lr_free_metalinkurl(LrMetalinkUrl *metalinkurl) { if (!metalinkurl) return; lr_free(metalinkurl->protocol); lr_free(metalinkurl->type); lr_free(metalinkurl->location); lr_free(metalinkurl->url); lr_free(metalinkurl); } static void lr_free_metalinkalternate(LrMetalinkAlternate *metalinkalternate) { if (!metalinkalternate) return; g_slist_free_full(metalinkalternate->hashes, (GDestroyNotify)lr_free_metalinkhash); lr_free(metalinkalternate); } LrMetalink * lr_metalink_init(void) { return lr_malloc0(sizeof(LrMetalink)); } void lr_metalink_free(LrMetalink *metalink) { if (!metalink) return; lr_free(metalink->filename); g_slist_free_full(metalink->hashes, (GDestroyNotify)lr_free_metalinkhash); g_slist_free_full(metalink->urls, (GDestroyNotify)lr_free_metalinkurl); g_slist_free_full(metalink->alternates, (GDestroyNotify)lr_free_metalinkalternate); lr_free(metalink); } /* Idea of parser implementation is borrowed from libsolv */ typedef enum { STATE_START, STATE_METALINK, STATE_FILES, STATE_FILE, STATE_TIMESTAMP, STATE_SIZE, STATE_VERIFICATION, STATE_HASH, STATE_ALTERNATES, STATE_ALTERNATE, STATE_ALTERNATE_TIMESTAMP, STATE_ALTERNATE_SIZE, STATE_ALTERNATE_VERIFICATION, STATE_ALTERNATE_HASH, STATE_RESOURCES, STATE_URL, NUMSTATES } LrState; /* Same states in the first column must be together */ static LrStatesSwitch stateswitches[] = { { STATE_START, "metalink", STATE_METALINK, 0 }, { STATE_METALINK, "files", STATE_FILES, 0 }, { STATE_FILES, "file", STATE_FILE, 0 }, { STATE_FILE, "mm0:timestamp", STATE_TIMESTAMP, 1 }, { STATE_FILE, "size", STATE_SIZE, 1 }, { STATE_FILE, "verification", STATE_VERIFICATION, 0 }, { STATE_FILE, "mm0:alternates", STATE_ALTERNATES, 0 }, { STATE_FILE, "resources", STATE_RESOURCES, 0 }, { STATE_VERIFICATION, "hash", STATE_HASH, 1 }, { STATE_ALTERNATES, "mm0:alternate", STATE_ALTERNATE, 0 }, { STATE_ALTERNATE, "mm0:timestamp", STATE_ALTERNATE_TIMESTAMP, 1 }, { STATE_ALTERNATE, "size", STATE_ALTERNATE_SIZE, 1 }, { STATE_ALTERNATE, "verification", STATE_ALTERNATE_VERIFICATION, 0 }, { STATE_ALTERNATE_VERIFICATION, "hash", STATE_ALTERNATE_HASH, 1 }, { STATE_RESOURCES, "url", STATE_URL, 1 }, { NUMSTATES, NULL, NUMSTATES, 0 } }; static void lr_metalink_start_handler(void *pdata, const xmlChar *xmlElement, const xmlChar **xmlAttr) { LrParserData *pd = pdata; LrStatesSwitch *sw; const char **attr = (const char **)xmlAttr; const char *element = (const char *)xmlElement; if (pd->err) return; // There was an error -> do nothing if (pd->depth != pd->statedepth) { // We are inside of unknown element pd->depth++; return; } pd->depth++; if (!pd->swtab[pd->state]) { // Current element should not have any sub elements return; } // Find current state by its name for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) if (!g_strcmp0(element, sw->ename)) break; if (sw->from != pd->state) { // No state for current element (unknown element) lr_xml_parser_warning(pd, LR_XML_WARNING_UNKNOWNTAG, "Unknown element \"%s\"", element); return; } // Update parser data pd->state = sw->to; pd->docontent = sw->docontent; pd->statedepth = pd->depth; pd->lcontent = 0; pd->content[0] = '\0'; if (pd->ignore && pd->state != STATE_FILE) return; /* Ignore all subelements of the current file element */ switch (pd->state) { case STATE_START: case STATE_METALINK: case STATE_FILES: break; case STATE_FILE: { assert(pd->metalink); assert(!pd->metalinkurl); assert(!pd->metalinkhash); const char *name = lr_find_attr("name", attr); if (!name) { g_debug("%s: Missing attribute \"name\" of file element", __func__); g_set_error(&pd->err, LR_METALINK_ERROR, LRE_MLXML, "Missing attribute \"name\" of file element"); break; } if (pd->found || g_strcmp0(name, pd->filename)) { pd->ignore = 1; break; } else { pd->ignore = 0; pd->found = 1; } pd->metalink->filename = g_strdup(name); break; } case STATE_TIMESTAMP: case STATE_SIZE: case STATE_VERIFICATION: case STATE_ALTERNATES: break; case STATE_ALTERNATE: assert(pd->metalink); assert(!pd->metalinkurl); assert(!pd->metalinkhash); assert(!pd->metalinkalternate); LrMetalinkAlternate *ma; ma = lr_new_metalinkalternate(pd->metalink); pd->metalinkalternate = ma; break; case STATE_ALTERNATE_TIMESTAMP: case STATE_ALTERNATE_SIZE: case STATE_ALTERNATE_VERIFICATION: break; case STATE_HASH: { assert(pd->metalink); assert(!pd->metalinkurl); assert(!pd->metalinkhash); assert(!pd->metalinkalternate); LrMetalinkHash *mh; const char *type = lr_find_attr("type", attr); if (!type) { // Type of the hash is not specifed -> skip it lr_xml_parser_warning(pd, LR_XML_WARNING_MISSINGATTR, "hash element doesn't have attribute \"type\""); break; } mh = lr_new_metalinkhash(pd->metalink); mh->type = g_strdup(type); pd->metalinkhash = mh; break; } case STATE_ALTERNATE_HASH: { assert(pd->metalink); assert(pd->metalinkalternate); assert(!pd->metalinkurl); assert(!pd->metalinkhash); LrMetalinkHash *mh; const char *type = lr_find_attr("type", attr); if (!type) { // Type of the hash is not specifed -> skip it lr_xml_parser_warning(pd, LR_XML_WARNING_MISSINGATTR, "hash element doesn't have attribute \"type\""); break; } mh = lr_new_metalinkalternate_hash(pd->metalinkalternate); mh->type = g_strdup(type); pd->metalinkhash = mh; break; } case STATE_RESOURCES: break; case STATE_URL: { assert(pd->metalink); assert(!pd->metalinkurl); assert(!pd->metalinkhash); const char *val; assert(!pd->metalinkurl); LrMetalinkUrl *url = lr_new_metalinkurl(pd->metalink); if ((val = lr_find_attr("protocol", attr))) url->protocol = g_strdup(val); if ((val = lr_find_attr("type", attr))) url->type = g_strdup(val); if ((val = lr_find_attr("location", attr))) url->location = g_strdup(val); if ((val = lr_find_attr("preference", attr))) { long long ll_val = lr_xml_parser_strtoll(pd, val, 0); if (ll_val < 0 || ll_val > 100) { lr_xml_parser_warning(pd, LR_XML_WARNING_BADATTRVAL, "Bad value (\"%s\") of \"preference\" attribute in url element" " (should be in range 0-100)", val); } else { url->preference = ll_val; } } pd->metalinkurl = url; break; } default: break; }; return; } static void lr_metalink_end_handler(void *pdata, G_GNUC_UNUSED const xmlChar *element) { LrParserData *pd = pdata; unsigned int state = pd->state; if (pd->err) return; // There was an error -> do nothing if (pd->depth != pd->statedepth) { // Back from the unknown state pd->depth--; return; } pd->depth--; pd->statedepth--; pd->state = pd->sbtab[pd->state]; pd->docontent = 0; if (pd->ignore && state != STATE_FILE) { // Ignore all subelements of the current file element return; } switch (state) { case STATE_START: case STATE_METALINK: case STATE_FILES: case STATE_FILE: case STATE_VERIFICATION: case STATE_ALTERNATES: case STATE_ALTERNATE_VERIFICATION: break; case STATE_RESOURCES: break; case STATE_TIMESTAMP: assert(pd->metalink); assert(!pd->metalinkurl); assert(!pd->metalinkhash); pd->metalink->timestamp = lr_xml_parser_strtoll(pd, pd->content, 0); break; case STATE_SIZE: assert(pd->metalink); assert(!pd->metalinkurl); assert(!pd->metalinkhash); pd->metalink->size = lr_xml_parser_strtoll(pd, pd->content, 0); break; case STATE_HASH: assert(pd->metalink); assert(!pd->metalinkurl); if (!pd->metalinkhash) { // If hash has no type break; } pd->metalinkhash->value = g_strdup(pd->content); pd->metalinkhash = NULL; break; case STATE_ALTERNATE: assert(pd->metalink); assert(pd->metalinkalternate); pd->metalinkalternate = NULL; break; case STATE_ALTERNATE_TIMESTAMP: assert(pd->metalink); assert(!pd->metalinkurl); assert(!pd->metalinkhash); assert(pd->metalinkalternate); pd->metalinkalternate->timestamp = lr_xml_parser_strtoll(pd, pd->content, 0); break; case STATE_ALTERNATE_SIZE: assert(pd->metalink); assert(!pd->metalinkurl); assert(!pd->metalinkhash); assert(pd->metalinkalternate); pd->metalinkalternate->size = lr_xml_parser_strtoll(pd, pd->content, 0); break; case STATE_ALTERNATE_HASH: assert(pd->metalink); assert(pd->metalinkalternate); assert(!pd->metalinkurl); if (!pd->metalinkhash) { // If hash has no type break; } pd->metalinkhash->value = g_strdup(pd->content); pd->metalinkhash = NULL; break; case STATE_URL: assert(pd->metalink); assert(pd->metalinkurl); assert(!pd->metalinkhash); pd->metalinkurl->url = g_strdup(pd->content); pd->metalinkurl = NULL; break; default: break; }; return; } gboolean lr_metalink_parse_file(LrMetalink *metalink, int fd, const char *filename, LrXmlParserWarningCb warningcb, void *warningcb_data, GError **err) { gboolean ret = TRUE; LrParserData *pd; XmlParser parser; GError *tmp_err = NULL; assert(metalink); assert(fd >= 0); assert(filename); assert(!err || *err == NULL); // Init memset(&parser, 0, sizeof(parser)); parser.startElement = lr_metalink_start_handler; parser.endElement = lr_metalink_end_handler; parser.characters = lr_char_handler; pd = lr_xml_parser_data_new(NUMSTATES); pd->parser = &parser; pd->state = STATE_START; pd->metalink = metalink; pd->filename = (char *) filename; pd->ignore = 1; pd->found = 0; pd->warningcb = warningcb; pd->warningcb_data = warningcb_data; for (LrStatesSwitch *sw = stateswitches; sw->from != NUMSTATES; sw++) { if (!pd->swtab[sw->from]) pd->swtab[sw->from] = sw; pd->sbtab[sw->to] = sw->from; } // Parsing ret = lr_xml_parser_generic(parser, pd, fd, &tmp_err); if (tmp_err) { g_propagate_error(err, tmp_err); goto err; } // Clean up if (!pd->found) { g_set_error(err, LR_METALINK_ERROR, LRE_MLBAD, "file \"%s\" was not found in metalink", filename); ret = FALSE; // The wanted file was not found in metalink } err: lr_xml_parser_data_free(pd); return ret; } librepo-1.12.1/librepo/metalink.h000066400000000000000000000065311372027637700166640ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_METALINK_H__ #define __LR_METALINK_H__ #include #include "xmlparser.h" G_BEGIN_DECLS /** \defgroup metalink Metalink parser * \addtogroup metalink * @{ */ /** Single checksum for the metalink target file. */ typedef struct { char *type; /*!< Type of checksum (e.g. "md5", "sha1", "sha256", ... */ char *value; /*!< Value of the checksum */ } LrMetalinkHash; /** Single metalink URL */ typedef struct { char *protocol; /*!< Mirror protocol "http", "ftp", "rsync", ... */ char *type; /*!< Mirror type "http", "ftp", "rsync", ... */ char *location; /*!< ISO 3166-1 alpha-2 code ("US", "CZ", ..) */ int preference; /*!< Integer number 1-100, higher is better */ char *url; /*!< URL to the target file */ } LrMetalinkUrl; /** Alternate */ typedef struct { gint64 timestamp; /*!< File timestamp */ gint64 size; /*!< File size */ GSList *hashes; /*!< List of pointers to LrMetalinkHashes (could be NULL) */ } LrMetalinkAlternate; /** Metalink */ typedef struct { char *filename; /*!< Filename */ gint64 timestamp; /*!< Repo timestamp (the highest ts in the repomd.xml) */ gint64 size; /*!< File size */ GSList *hashes; /*!< List of pointers to LrMetalinkHashes (could be NULL) */ GSList *urls; /*!< List of pointers to LrMetalinkUrls (could be NULL) */ GSList *alternates; /*!< List of pointers to LrMetalinkAlternates (could be NULL) */ } LrMetalink; /** Create new empty metalink object. * @return New metalink object. */ LrMetalink * lr_metalink_init(void); /** Parse metalink file. * @param metalink Metalink object. * @param fd File descriptor. * @param filename File to look for in metalink file. * @param warningcb ::LrXmlParserWarningCb function or NULL * @param warningcb_data Warning callback data or NULL * @param err GError ** * @return TRUE if everything is ok, FALSE if err is set. */ gboolean lr_metalink_parse_file(LrMetalink *metalink, int fd, const char *filename, LrXmlParserWarningCb warningcb, void *warningcb_data, GError **err); /** Free metalink object and all its content. * @param metalink Metalink object. */ void lr_metalink_free(LrMetalink *metalink); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/mirrorlist.c000066400000000000000000000053561372027637700172650ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _POSIX_SOURCE #include #include #include #include #include #include "rcodes.h" #include "util.h" #include "mirrorlist.h" #define BUF_LEN 4096 LrMirrorlist * lr_mirrorlist_init(void) { return lr_malloc0(sizeof(LrMirrorlist)); } void lr_mirrorlist_free(LrMirrorlist *mirrorlist) { if (!mirrorlist) return; g_slist_free_full(mirrorlist->urls, (GDestroyNotify)lr_free); lr_free(mirrorlist); } gboolean lr_mirrorlist_parse_file(LrMirrorlist *mirrorlist, int fd, GError **err) { FILE *f; int fd_dup; char buf[BUF_LEN], *p; assert(mirrorlist); assert(fd >= 0); assert(!err || *err == NULL); fd_dup = dup(fd); if (fd_dup == -1) { g_set_error(err, LR_MIRRORLIST_ERROR, LRE_IO, "dup(%d) error: %s", fd, g_strerror(errno)); return FALSE; } f = fdopen(fd_dup, "r"); if (!f) { g_debug("%s: Cannot fdopen(mirrorlist_fd): %s", __func__, g_strerror(errno)); g_set_error(err, LR_MIRRORLIST_ERROR, LRE_IO, "fdopen(%d, \"r\") error: %s", fd_dup, g_strerror(errno)); return FALSE; } while ((p = fgets(buf, BUF_LEN, f))) { int l; /* Skip leading white characters */ while (*p == ' ' || *p == '\t') p++; if (!*p || *p == '#') continue; /* End of string or comment */ l = strlen(p); /* Remove trailing white characters */ while (l > 0 && (p[l-1] == ' ' || p[l-1] == '\n' || p[l-1] == '\t')) l--; p[l] = '\0'; if (!l) continue; /* Append URL */ if (p[0] != '\0' && (strstr(p, "://") || p[0] == '/')) mirrorlist->urls = g_slist_append(mirrorlist->urls, g_strdup(p)); } fclose(f); return TRUE; } librepo-1.12.1/librepo/mirrorlist.h000066400000000000000000000035031372027637700172620ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_MIRRORLIST_H__ #define __LR_MIRRORLIST_H__ #include G_BEGIN_DECLS /** \defgroup mirrorlist Mirrorlist parser * \addtogroup mirrorlist * @{ */ /** Mirrorlist */ typedef struct { GSList *urls; /*!< List URLs (char *), could be NULL */ } LrMirrorlist; /** * Create new empty mirrorlist. * @return New empty mirrorlist. */ LrMirrorlist * lr_mirrorlist_init(void); /** * Parse mirrorlist file. * @param mirrorlist Mirrorlist object. * @param fd Opened file descriptor of mirrorlist file. * @param err GError ** * @return TRUE if everything is ok, FALSE if err is set. */ gboolean lr_mirrorlist_parse_file(LrMirrorlist *mirrorlist, int fd, GError **err); /** * Free mirrorlist and all its content. * @param mirrorlist Mirrorlist object. */ void lr_mirrorlist_free(LrMirrorlist *mirrorlist); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/package_downloader.c000066400000000000000000000562611372027637700206710ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE // for GNU basename() implementation from string.h #include #include #include #include #include #include #include #include #include #include "types.h" #include "cleanup.h" #include "util.h" #include "package_downloader.h" #include "handle_internal.h" #include "downloader.h" #include "fastestmirror_internal.h" /* Do NOT use resume on successfully downloaded files - download will fail */ LrPackageTarget * lr_packagetarget_new(LrHandle *handle, const char *relative_url, const char *dest, LrChecksumType checksum_type, const char *checksum, gint64 expectedsize, const char *base_url, gboolean resume, LrProgressCb progresscb, void *cbdata, GError **err) { LrPackageTarget *target; assert(relative_url); assert(!err || *err == NULL); target = lr_malloc0(sizeof(*target)); if (!target) { g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_MEMORY, "Out of memory"); return NULL; } target->chunk = g_string_chunk_new(16); target->handle = handle; target->relative_url = lr_string_chunk_insert(target->chunk, relative_url); target->dest = lr_string_chunk_insert(target->chunk, dest); target->checksum_type = checksum_type; target->checksum = lr_string_chunk_insert(target->chunk, checksum); target->expectedsize = expectedsize; target->base_url = lr_string_chunk_insert(target->chunk, base_url); target->resume = resume; target->progresscb = progresscb; target->cbdata = cbdata; return target; } LrPackageTarget * lr_packagetarget_new_v2(LrHandle *handle, const char *relative_url, const char *dest, LrChecksumType checksum_type, const char *checksum, gint64 expectedsize, const char *base_url, gboolean resume, LrProgressCb progresscb, void *cbdata, LrEndCb endcb, LrMirrorFailureCb mirrorfailurecb, GError **err) { LrPackageTarget *target; target = lr_packagetarget_new(handle, relative_url, dest, checksum_type, checksum, expectedsize, base_url, resume, progresscb, cbdata, err); if (!target) return NULL; target->endcb = endcb; target->mirrorfailurecb = mirrorfailurecb; return target; } LrPackageTarget * lr_packagetarget_new_v3(LrHandle *handle, const char *relative_url, const char *dest, LrChecksumType checksum_type, const char *checksum, gint64 expectedsize, const char *base_url, gboolean resume, LrProgressCb progresscb, void *cbdata, LrEndCb endcb, LrMirrorFailureCb mirrorfailurecb, gint64 byterangestart, gint64 byterangeend, GError **err) { LrPackageTarget *target; target = lr_packagetarget_new_v2(handle, relative_url, dest, checksum_type, checksum, expectedsize, base_url, resume, progresscb, cbdata, endcb, mirrorfailurecb, err); if (!target) return NULL; target->byterangestart = byterangestart; target->byterangeend = byterangeend; return target; } void lr_packagetarget_reset(LrPackageTarget *target) { target->local_path = NULL; target->err = NULL; } void lr_packagetarget_free(LrPackageTarget *target) { if (!target) return; g_string_chunk_free(target->chunk); g_free(target); } gboolean lr_download_packages(GSList *targets, LrPackageDownloadFlag flags, GError **err) { gboolean ret; gboolean failfast = flags & LR_PACKAGEDOWNLOAD_FAILFAST; struct sigaction old_sigact; GSList *downloadtargets = NULL; gboolean interruptible = FALSE; assert(!err || *err == NULL); if (!targets) return TRUE; // Check targets for (GSList *elem = targets; elem; elem = g_slist_next(elem)) { LrPackageTarget *packagetarget = elem->data; if (!packagetarget->handle) { continue; /* g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_BADFUNCARG, "Package target %s doesn't have specified a handle", packagetarget->relative_url); return FALSE; */ } if (packagetarget->handle->interruptible) interruptible = TRUE; // Check repotype // Note: Checked because lr_handle_prepare_internal_mirrorlist // support only LR_YUMREPO yet if (packagetarget->handle->repotype != LR_YUMREPO) { g_debug("%s: Bad repo type", __func__); g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_BADFUNCARG, "Bad repo type"); return FALSE; } } // Setup sighandler if (interruptible) { struct sigaction sigact; g_debug("%s: Using own SIGINT handler", __func__); memset(&sigact, 0, sizeof(old_sigact)); memset(&sigact, 0, sizeof(sigact)); sigemptyset(&sigact.sa_mask); sigact.sa_handler = lr_sigint_handler; sigaddset(&sigact.sa_mask, SIGINT); sigact.sa_flags = SA_RESTART; if (sigaction(SIGINT, &sigact, &old_sigact) == -1) { g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_SIGACTION, "Cannot set Librepo SIGINT handler"); return FALSE; } } // List of handles for fastest mirror resolving GSList *fmr_handles = NULL; // Prepare targets for (GSList *elem = targets; elem; elem = g_slist_next(elem)) { _cleanup_free_ gchar *local_path = NULL; LrPackageTarget *packagetarget = elem->data; LrDownloadTarget *downloadtarget; gint64 realsize = -1; gboolean doresume = packagetarget->resume; // Reset output attributes of the handle lr_packagetarget_reset(packagetarget); // Prepare destination filename if (packagetarget->dest) { if (g_file_test(packagetarget->dest, G_FILE_TEST_IS_DIR)) { // Dir specified // unencode first in case there are any encoded slashes to // prevent any path changing shenanigans _cleanup_free_ gchar * unencoded_url = g_uri_unescape_string(packagetarget->relative_url, ""); _cleanup_free_ gchar * file_basename = g_path_get_basename(unencoded_url); local_path = g_build_filename(packagetarget->dest, file_basename, NULL); } else { local_path = g_strdup(packagetarget->dest); } } else { // No destination path specified // unencode first in case there are any encoded slashes to // prevent any path changing shenanigans _cleanup_free_ gchar * unencoded_url = g_uri_unescape_string(packagetarget->relative_url, ""); local_path = g_path_get_basename(unencoded_url); } packagetarget->local_path = g_string_chunk_insert(packagetarget->chunk, local_path); // Check expected size and real size if the file exists if (doresume && g_access(packagetarget->local_path, R_OK) == 0 && packagetarget->expectedsize > 0) { struct stat buf; if (stat(packagetarget->local_path, &buf)) { g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_IO, "Cannot stat %s: %s", packagetarget->local_path, g_strerror(errno)); return FALSE; } realsize = buf.st_size; if (packagetarget->expectedsize < realsize) // Existing file is bigger then the one that is expected, // disable resuming doresume = FALSE; } if (g_access(packagetarget->local_path, R_OK) == 0 && packagetarget->checksum && packagetarget->checksum_type != LR_CHECKSUM_UNKNOWN) { /* If the file exists and checksum is ok, then is pointless to * download the file again. * Moreover, if the resume is enabled and the file is already * completely downloaded, then the download is going to fail. */ int fd_r = open(packagetarget->local_path, O_RDONLY); if (fd_r != -1) { gboolean matches; ret = lr_checksum_fd_cmp(packagetarget->checksum_type, fd_r, packagetarget->checksum, 1, &matches, NULL); close(fd_r); if (ret && matches) { // Checksum calculation was ok and checksum matches g_debug("%s: Package %s is already downloaded (checksum matches)", __func__, packagetarget->local_path); packagetarget->err = g_string_chunk_insert( packagetarget->chunk, "Already downloaded"); // Call end callback LrEndCb end_cb = packagetarget->endcb; if (end_cb) end_cb(packagetarget->cbdata, LR_TRANSFER_ALREADYEXISTS, "Already downloaded"); continue; } else if (ret) { // Checksum calculation was ok but checksum doesn't match if (realsize != -1 && realsize == packagetarget->expectedsize) // File size is the same as the expected one // Don't try to resume doresume = FALSE; } } } if (doresume && realsize != -1 && realsize == packagetarget->expectedsize) { // File's size matches the expected one, the resume is enabled and // no checksum is known => expect that the file is // the one the user wants g_debug("%s: Package %s is already downloaded (size matches)", __func__, packagetarget->local_path); packagetarget->err = g_string_chunk_insert( packagetarget->chunk, "Already downloaded"); // Call end callback LrEndCb end_cb = packagetarget->endcb; if (end_cb) end_cb(packagetarget->cbdata, LR_TRANSFER_ALREADYEXISTS, "Already downloaded"); continue; } if (packagetarget->handle) { ret = lr_handle_prepare_internal_mirrorlist(packagetarget->handle, FALSE, err); if (!ret) goto cleanup; if (packagetarget->handle->fastestmirror) { if (!g_slist_find(fmr_handles, packagetarget->handle)) fmr_handles = g_slist_prepend(fmr_handles, packagetarget->handle); } } GSList *checksums = NULL; LrDownloadTargetChecksum *checksum; checksum = lr_downloadtargetchecksum_new(packagetarget->checksum_type, packagetarget->checksum); checksums = g_slist_prepend(checksums, checksum); downloadtarget = lr_downloadtarget_new(packagetarget->handle, packagetarget->relative_url, packagetarget->base_url, -1, packagetarget->local_path, checksums, packagetarget->expectedsize, doresume, packagetarget->progresscb, packagetarget->cbdata, packagetarget->endcb, packagetarget->mirrorfailurecb, packagetarget, packagetarget->byterangestart, packagetarget->byterangeend, NULL, FALSE, FALSE); downloadtargets = g_slist_append(downloadtargets, downloadtarget); } // Do Fastest Mirror resolving for all handles in one shot if (fmr_handles) { fmr_handles = g_slist_reverse(fmr_handles); ret = lr_fastestmirror_sort_internalmirrorlists(fmr_handles, err); g_slist_free(fmr_handles); if (!ret) { return FALSE; } } // Start downloading ret = lr_download(downloadtargets, failfast, err); cleanup: // Copy download statuses from downloadtargets to targets for (GSList *elem = downloadtargets; elem; elem = g_slist_next(elem)) { LrDownloadTarget *downloadtarget = elem->data; LrPackageTarget *packagetarget = downloadtarget->userdata; if (downloadtarget->err) packagetarget->err = g_string_chunk_insert(packagetarget->chunk, downloadtarget->err); } // Free downloadtargets list g_slist_free_full(downloadtargets, (GDestroyNotify)lr_downloadtarget_free); // Restore original signal handler if (interruptible) { g_debug("%s: Restoring an old SIGINT handler", __func__); sigaction(SIGINT, &old_sigact, NULL); if (lr_interrupt) { if (err && *err != NULL) g_clear_error(err); g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_INTERRUPTED, "Interrupted by a SIGINT signal"); return FALSE; } } return ret; } gboolean lr_download_package(LrHandle *handle, const char *relative_url, const char *dest, LrChecksumType checksum_type, const char *checksum, gint64 expectedsize, const char *base_url, gboolean resume, GError **err) { LrPackageTarget *target; assert(handle); assert(!err || *err == NULL); // XXX: Maybe remove in future if (!dest) dest = handle->destdir; // XXX: Maybe remove usage of handle callback in future target = lr_packagetarget_new(handle, relative_url, dest, checksum_type, checksum, expectedsize, base_url, resume, handle->user_cb, handle->user_data, err); if (!target) return FALSE; GSList *targets = NULL; targets = g_slist_append(targets, target); gboolean ret = lr_download_packages(targets, LR_PACKAGEDOWNLOAD_FAILFAST, err); g_slist_free_full(targets, (GDestroyNotify)lr_packagetarget_free); return ret; } gboolean lr_check_packages(GSList *targets, LrPackageCheckFlag flags, GError **err) { gboolean ret = TRUE; gboolean failfast = flags & LR_PACKAGECHECK_FAILFAST; struct sigaction old_sigact; gboolean interruptible = FALSE; assert(!err || *err == NULL); if (!targets) return TRUE; // Check targets for (GSList *elem = targets; elem; elem = g_slist_next(elem)) { LrPackageTarget *packagetarget = elem->data; if (packagetarget->handle->interruptible) interruptible = TRUE; if (!packagetarget->checksum || packagetarget->checksum_type == LR_CHECKSUM_UNKNOWN) { g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_BADOPTARG, "Target %s doesn't have specified " "checksum value or checksum type!", packagetarget->relative_url); return FALSE; } } // Setup sighandler if (interruptible) { g_debug("%s: Using own SIGINT handler", __func__); struct sigaction sigact; sigact.sa_handler = lr_sigint_handler; sigaddset(&sigact.sa_mask, SIGINT); sigact.sa_flags = SA_RESTART; if (sigaction(SIGINT, &sigact, &old_sigact) == -1) { g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_SIGACTION, "Cannot set Librepo SIGINT handler"); return FALSE; } } for (GSList *elem = targets; elem; elem = g_slist_next(elem)) { _cleanup_free_ gchar *local_path = NULL; LrPackageTarget *packagetarget = elem->data; // Prepare destination filename if (packagetarget->dest) { if (g_file_test(packagetarget->dest, G_FILE_TEST_IS_DIR)) { // Dir specified _cleanup_free_ gchar *file_basename; file_basename = g_path_get_basename(packagetarget->relative_url); local_path = g_build_filename(packagetarget->dest, file_basename, NULL); } else { local_path = g_strdup(packagetarget->dest); } } else { // No destination path specified local_path = g_path_get_basename(packagetarget->relative_url); } packagetarget->local_path = g_string_chunk_insert(packagetarget->chunk, local_path); if (g_access(packagetarget->local_path, R_OK) == 0) { // If the file exists check its checksum int fd_r = open(packagetarget->local_path, O_RDONLY); if (fd_r != -1) { // File was successfully opened gboolean matches; ret = lr_checksum_fd_cmp(packagetarget->checksum_type, fd_r, packagetarget->checksum, 1, &matches, NULL); close(fd_r); if (ret && matches) { // Checksum is ok packagetarget->err = NULL; g_debug("%s: Package %s is already downloaded (checksum matches)", __func__, packagetarget->local_path); } else { // Checksum doesn't match or checksumming error packagetarget->err = g_string_chunk_insert( packagetarget->chunk, "Checksum of file doesn't match"); if (failfast) { ret = FALSE; g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_BADCHECKSUM, "File with nonmatching checksum found"); break; } } } else { // Cannot open the file packagetarget->err = g_string_chunk_insert(packagetarget->chunk, "Cannot be opened"); if (failfast) { ret = FALSE; g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_IO, "Cannot open %s", packagetarget->local_path); break; } } } else { // File doesn't exists packagetarget->err = g_string_chunk_insert(packagetarget->chunk, "Doesn't exist"); if (failfast) { ret = FALSE; g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_IO, "File %s doesn't exists", packagetarget->local_path); break; } } } // Restore original signal handler if (interruptible) { g_debug("%s: Restoring an old SIGINT handler", __func__); sigaction(SIGINT, &old_sigact, NULL); if (lr_interrupt) { if (err && *err != NULL) g_clear_error(err); g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_INTERRUPTED, "Interrupted by a SIGINT signal"); return FALSE; } } return ret; } librepo-1.12.1/librepo/package_downloader.h000066400000000000000000000306121372027637700206660ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_PACKAGE_DOWNLOADER_H__ #define __LR_PACKAGE_DOWNLOADER_H__ #include #include "rcodes.h" #include "handle.h" #include "checksum.h" G_BEGIN_DECLS /** \defgroup package_downloader Package downloading * \addtogroup package_downloader * @{ */ /** Download package from repository. * @param handle Librepo handle. * @param relative_url Relative part of url. * @param err GError ** * @return See ::lr_download_package */ #define lr_download_simple(handle, relative_url, err) \ lr_download_package((handle), (relative_url), NULL, 0, \ NULL, 0, NULL, 0, (err)) /** Download package from repository or base_url. * Note: If resume, checksum and checksum_type are specified and * the downloaded package already exists and checksum matches, then * no downloading is done and LRE_ALREADYDOWNLOADED return code is returned. * @param handle Librepo handle. * @param relative_url Relative part of url. * @param dest Destination file, directory * or NULL (current working dir is used). * @param checksum_type Type of checksum. * @param checksum Checksum value or NULL. * @param expectedsize Expected size of target. If >0 and server reports * different size, then no download is performed. * @param base_url If specified, mirrors from handle are ignored * and this base_url is used for downloading. * @param resume If TRUE try to resume downloading if dest file * already exists. * @param err GError ** * @return TRUE if everything is ok, FALSE if err is set. */ gboolean lr_download_package(LrHandle *handle, const char *relative_url, const char *dest, LrChecksumType checksum_type, const char *checksum, gint64 expectedsize, const char *base_url, gboolean resume, GError **err); /** LrPackageTarget structure */ typedef struct { LrHandle *handle; /*!< Related handle */ char *relative_url; /*!< Relative part of URL */ char *dest; /*!< Destination: filename, dirname or NULL */ char *base_url; /*!< Base URL for this target */ LrChecksumType checksum_type; /*!< Checksum type */ char *checksum; /*!< Expected checksum value */ gint64 expectedsize; /*!< Expected size of the target */ gboolean resume; /*!< Indicate if resume is enabled */ LrProgressCb progresscb; /*!< Progress callback */ void *cbdata; /*!< Callback data */ LrEndCb endcb; /*!< Callback called when target transfer is done. (Use status to check if successfully or unsuccessfully) */ LrMirrorFailureCb mirrorfailurecb; /*!< Called when download from a mirror failed. */ gint64 byterangestart; /*!< Download only specified range of bytes. */ gint64 byterangeend; /*!< Download only specified range of bytes. */ // Will be filled by ::lr_download_packages() char *local_path; /*!< Local path */ char *err; /*!< Error message or NULL. NULL means no error. */ GStringChunk *chunk; /*!< String chunk */ } LrPackageTarget; /** Create new LrPackageTarget object. * @param handle Handle related to this download or NULL. * @param relative_url Relative part of URL to download. * First part of URL will be picked from the LrHandle * (LRO_URL or mirror) during download process or * base_url will be used if it is specified. It is * expected to already come URL-encoded. * @param dest Destination filename or just directory (filename * itself will be derived from the relative_url) or * NULL (current working directory + filename derived * from relative_url will be used). * @param checksum_type Type of checksum or LR_CHECKSUM_UNKNOWN. * @param checksum Expected checksum value or NULL. * @param base_url Base URL or NULL * @param expectedsize Expected size of the target. If server reports * different size, then download won't be performed. * @param resume If TRUE, then downloader will try to resume download * if the destination file exists. If the file doesn't * exist it will be downloaded. * @param progresscb Progress callback for this transfer. * @param cbdata User data for the callback * @param err GError ** * @return Newly allocated LrPackageTarget or NULL on error */ LrPackageTarget * lr_packagetarget_new(LrHandle *handle, const char *relative_url, const char *dest, LrChecksumType checksum_type, const char *checksum, gint64 expectedsize, const char *base_url, gboolean resume, LrProgressCb progresscb, void *cbdata, GError **err); /** Create new LrPackageTarget object. * Almost same as lr_packagetarget_new() except this function * could set more callbacks. * @param handle Handle related to this download or NULL. * @param relative_url Relative part of URL to download. * First part of URL will be picked from the LrHandle * (LRO_URL or mirror) during download process or * base_url will be used if it is specified. * @param dest Destination filename or just directory (filename * itself will be derived from the relative_url) or * NULL (current working directory + filename derived * from relative_url will be used). * @param checksum_type Type of checksum or LR_CHECKSUM_UNKNOWN. * @param checksum Expected checksum value or NULL. * @param base_url Base URL or NULL * @param expectedsize Expected size of the target. If server reports * different size, then no download is performed. * If 0 then size check is ignored. * @param resume If TRUE, then downloader try to resume download * if destination file exists. If the file doesn't * exists, it will be normally downloaded again. * @param progresscb Progress callback for this transfer. * @param cbdata User data for the callbacks * @param endcb Callback called when target transfer is done. * (Use status to check if successfully * @param mirrorfailurecb Called when download from a mirror failed. * @param err GError ** * @return Newly allocated LrPackageTarget or NULL on error */ LrPackageTarget * lr_packagetarget_new_v2(LrHandle *handle, const char *relative_url, const char *dest, LrChecksumType checksum_type, const char *checksum, gint64 expectedsize, const char *base_url, gboolean resume, LrProgressCb progresscb, void *cbdata, LrEndCb endcb, LrMirrorFailureCb mirrorfailurecb, GError **err); /** Create new LrPackageTarget object. * Almost same as lr_packagetarget_new_v2() except this function * could set a required byte range of the package. * For params see lr_packagetarget_new_v2(). * @param byterangestart Download only specified range of bytes. This param * specifies the begin. 0 is default. * Note: When this options is != 0 then resume must * be disabled - resume param must be FALSE. * @param byterangeend Download only specified range of bytes. This param * specifies the end. 0 is default. * If this value is less or equal to byterangestart, * then it is ignored. * @return Newly allocated LrPackageTarget or NULL on error */ LrPackageTarget * lr_packagetarget_new_v3(LrHandle *handle, const char *relative_url, const char *dest, LrChecksumType checksum_type, const char *checksum, gint64 expectedsize, const char *base_url, gboolean resume, LrProgressCb progresscb, void *cbdata, LrEndCb endcb, LrMirrorFailureCb mirrorfailurecb, gint64 byterangestart, gint64 byterangeend, GError **err); /** Free ::LrPackageTarget object. * @param target LrPackageTarget object */ void lr_packagetarget_free(LrPackageTarget *target); /** Available flags for package downloader */ typedef enum { LR_PACKAGEDOWNLOAD_FAILFAST = 1 << 0, /*!< If TRUE, then whole downloading is stopped immediately when any of download fails (FALSE is returned and err is set). If the failfast is FALSE, then this function returns after all downloads finish (no matter if successfully or unsuccessfully) and FALSE is returned only if a nonrecoverable error related to the function itself is meet (Errors related to individual downloads are reported via corresponding PackageTarget objects). */ } LrPackageDownloadFlag; /** Download all LrPackageTargets at the targets GSList. * @param targets GSList where each element is a ::LrPackageTarget * object * @param flags Bitfield with flags to download * @param err GError ** * @return If FALSE then err is set. */ gboolean lr_download_packages(GSList *targets, LrPackageDownloadFlag flags, GError **err); typedef enum { LR_PACKAGECHECK_FAILFAST = 1 << 0, /*!< If TRUE, then whole check is stoped immediately when any of target doesn't exist locally or its checksum doesn't match. (FALSE is returned and err is set). If the failfast is FALSE, then this function returns after check of all targets is finished (no matter if successfully or unsuccessfully) and FALSE is returned only if a nonrecoverable error related to the function itself is meet (Errors related to individual targets are reported via corresponding PackageTarget objects). */ } LrPackageCheckFlag; /** Check if targets locally exist and checksums match. * If target locally exists, then its err is NULL, * if it doesn't exists, or checksum is differ. Then target->err is * an error message. */ gboolean lr_check_packages(GSList *targets, LrPackageCheckFlag flags, GError **err); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/python/000077500000000000000000000000001372027637700162235ustar00rootroot00000000000000librepo-1.12.1/librepo/python/CMakeLists.txt000066400000000000000000000012401372027637700207600ustar00rootroot00000000000000SET(pylibrepo_SRCDIR ${CMAKE_CURRENT_SOURCE_DIR}) SET (librepomodule_SRCS ${pylibrepo_SRCDIR}/downloader-py.c ${pylibrepo_SRCDIR}/exception-py.c ${pylibrepo_SRCDIR}/handle-py.c ${pylibrepo_SRCDIR}/librepomodule.c ${pylibrepo_SRCDIR}/logging.c ${pylibrepo_SRCDIR}/metadatadownloader-py.c ${pylibrepo_SRCDIR}/metadatatarget-py.c ${pylibrepo_SRCDIR}/packagedownloader-py.c ${pylibrepo_SRCDIR}/packagetarget-py.c ${pylibrepo_SRCDIR}/result-py.c ${pylibrepo_SRCDIR}/typeconversion.c ${pylibrepo_SRCDIR}/yum-py.c) MESSAGE("Building for python${PYTHON_VERSION_MAJOR}") ADD_SUBDIRECTORY(python${PYTHON_VERSION_MAJOR}) librepo-1.12.1/librepo/python/__init__.py000066400000000000000000001511341372027637700203410ustar00rootroot00000000000000# librepo - A library providing (libcURL like) API to downloading repository # Copyright (C) 2012-2014 Tomas Mlcoch # # Licensed under the GNU Lesser General Public License Version 2.1 # # 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 2.1 of the License, or (at your option) any later version. # # This library 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, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # """ Version contants ---------------- .. data:: VERSION_MAJOR VERSION_MINOR VERSION_PATCH VERSION Constants with version numbers and whole version string. .. _handle-options-label: :class:`~.Handle` options ------------------------- **LRO_** (aka LibRepo Option) prefixed constants are used to set :class:`.Handle` options via :meth:`~.Handle.setopt` method. This options could be also set by :class:`.Handle` attributes with the same names but in lowercase and without ``LRO_`` prefix. Example:: # The command: h.setopt(librepo.LRO_URLS, ["http://ftp.linux.ncsu.edu/pub/fedora/linux/releases/17/Everything/i386/os/"]) # is equivalent to: h.urls = ["http://ftp.linux.ncsu.edu/pub/fedora/linux/releases/17/Everything/i386/os/"] .. note:: For detailed description of this options consult :class:`.Handle` page. .. data:: LRO_UPDATE *Boolean*. Set to ``True`` if only want update localised or downloaded repository represented by :class:`.Result` object. Update mode is meant to download previously omitted repository file(s). .. data:: LRO_URLS *List or None*. Set repository urls (repository url and baseurl are interchangeable terms in this context). .. data:: LRO_MIRRORLIST *String or None*. **DEPRECATED** Set mirrorlist url (url could point to a metalink mirrorlist or to a simple mirrorlist where each line wihtout ``'#'`` is considered as mirror url). .. data:: LRO_MIRRORLISTURL *String or None*. Mirrorlist URL. .. data:: LRO_METALINKURL *String or None*. Metalink URL. .. data:: LRO_LOCAL *Boolean*. If set to True, no local copy of repository is created and repository is just localised in its current location. When True, url of repository MUST be a local address (e.g. '/home/user/repo' or 'file:///home/user/repo'). .. data:: LRO_HTTPAUTH *Boolean*. If True, all supported methods of HTTP authentication are enabled. .. data:: LRO_USERPWD *String or None*. Set username and password for HTTP authentication. Param must be in format 'username:password'. .. data:: LRO_PROXY *String or None*. Set proxy server address. Port could be specified by different option or by the :[port] suffix of this address. Any protocol prefix (``http://``, ...) will be ignored. .. data:: LRO_PROXYPORT *Integer or None*. Set proxy port number to connect unsless it is specified in the proxy address string. None sets default value 1080. .. data:: LRO_PROXYTYPE *Long*. Set type of proxy - could be one of :ref:`proxy-type-label` .. data:: LRO_PROXYAUTH *Boolean*. If True, all supported proxy authentication methods are enabled. If False, only basic authentication method is enabled. .. data:: LRO_PROXYUSERPWD *String or None*. Set username and password for proxy authentication in format 'username:password'. .. data:: LRO_PROGRESSCB *Function or None*. (See: :ref:`callback-progresscb-label`) Set progress callback. Callback must be in format: ``callback(userdata, total_to_download, downloaded)``. If total_to_download is 0, then total size is not known. Total size (total_to_download) could change (grow) among two callback calls (if some download failed and another mirror is tried). .. data:: LRO_PROGRESSDATA *Any object*. Set user data for the progress callback. .. data:: LRO_MAXSPEED *Long or None*. Set maximal allowed speed per download in bytes per second. 0 = unlimited speed - the default value. .. data:: LRO_DESTDIR *String or None*. Set destination directory for downloaded data (metadata or packages). .. data:: LRO_REPOTYPE *Integer*. One of :ref:`repotype-constants-label`. See more :meth:`~.Handle.repotype` Set type of repository. .. data:: LRO_CONNECTTIMEOUT *Integer or None. Set maximal timeout in sec for connection phase. Default value is 30. None as *val* sets the default value. .. data:: LRO_IGNOREMISSING *Boolean*. If you want to localise (LRO_LOCAL is True) a incomplete local repository (eg. only primary and filelists are present but repomd.xml contains more files), you could use LRO_YUMDLIST and specify only file that are present, or use LRO_YUMBLIST and specify files that are not present or use this option. .. data:: LRO_INTERRUPTIBLE *Boolean*. Librepo sets up its own SIGTERM handler. If the SIGTERM signal is catched, the current download is interrupted. .. data:: LRO_USERAGENT *String*. String for User-Agent: header in the http request sent to the remote server. .. data:: LRO_FETCHMIRRORS *Boolean*. With this option enabled, only mirrorlist download and parsing will be performed during :meth:`librepo.Handle.perform`. .. data:: LRO_MAXMIRRORTRIES *Integer or None*. If download fails, try at most the specified number of mirrors. 0 (None) means try all available mirrors. .. data:: LRO_MAXPARALLELDOWNLOADS *Integer or None*. Maximum number of parallel downloads. ``None`` sets default value. .. data:: LRO_MAXDOWNLOADSPERMIRROR *Integer or None*. Maximum number of parallel downloads per mirror. ``None`` sets default value. .. data:: LRO_VARSUB *[(String, String), ...] or None*. Set list of substitutions for variables in ulrs (e.g.: "http://foo/$version/"). ``[("releasever", "f18"), ("basearch", "i386")]`` .. data:: LRO_FASTESTMIRROR *Boolean*. If True, internal mirrorlist is sorted by the determined connection speed, after it is constructed. .. data:: LRO_FASTESTMIRRORCACHE *String or None*. Path to the cache file. If cache file it doesn't exists it will be created. .. data:: LRO_FASTESTMIRRORMAXAGE *Integer or None*. Max age of cache record. Older records will not be used. .. data:: LRO_FASTESTMIRRORCB *Function or None*. (See: :ref:`callback-fastestmirrorcb-label`) Fastest mirror status callback. Its prototype looks like ``callback(userdata, stage, data)`` Where *userdata* are data passed by user via LRO_FASTESTMIRRORDATA. *stage* is value from :ref:`fastestmirror-stages-constants-label`. *data* value depends on *stage* value. See the list of available stages. .. data:: LRO_FASTESTMIRRORDATA *Any object*. User data for fastest mirror status callback. .. data:: LRO_LOWSPEEDTIME *Integer or None*. The time in seconds that the transfer should be below the LRO_LOWSPEEDLIMIT for the library to consider it too slow and abort. Default: 30 (sec) .. data:: LRO_LOWSPEEDLIMIT *Integer or None*. The transfer speed in bytes per second that the transfer should be below during LRO_LOWSPEEDTIME seconds for the library to consider it too slow and abort. Default: 1000 (byte/s) .. data:: LRO_GPGCHECK *Boolean*. Set True to enable gpg check (if available) of downloaded repo. .. data:: LRO_CHECKSUM *Boolean*. Set False/True to disable/enable checksum check. .. note:: Checksum check is enabled by default. .. note:: If checksum check is disabled, then even explicitly specified checksum related params e.g. in :meth:`~librepo.Handle.download` method are ignored and checksum is not checked! .. data:: LRO_YUMDLIST *List of strings*. Some predefined list :ref:`predefined-yumdlists-label`. Set list of yum metadata files to download. e.g. ``["primary", "filelists", "other", "primary_db", "prestodelta"]`` If *val* is None, all metadata files will be downloaded. If *val* is ``[]`` or ``[None]`` only ``repomd.xml`` will be downloaded. .. data:: LRO_YUMSLIST *[(String, String), ...] or None*. Set list of substitutions for repomd records. ``[("group_gz", "group")]` .. data:: LRO_RPMMDDLIST See LRO_YUMDLIST .. data:: LRO_YUMBLIST *List of strings*. Set blacklist of yum metadata files. This files will not be downloaded. .. data:: LRO_RPMMDBLIST See LRO_YUMBLIST .. data:: LRO_HMFCB *Function or None* (See: :ref:`callback-handlemirrorfailurecb-label`) The Handle Mirror Failure Callback is called when a metadata download fails. It provides a detailed information about what exactly failed. Call of this callback doesn't mean that whole downloading failed. If there are other mirrors on the list, these mirrors will be tried. .. data:: LRO_SSLVERIFYPEER *Boolean*. This option determines whether librepo verifies the authenticity of the peer's certificate. This trust is based on a chain of digital signatures, rooted in certification authority (CA) certificates. .. data:: LRO_SSLVERIFYHOST *Boolean*. This option determines whether librepo verifies that the server cert is for the server it is known as. .. data:: LRO_SSLCLIENTCERT *String or None*. Path to the PEM format SSL client certificate to use when talking to the server. .. data:: LRO_SSLCLIENTKEY *String or None*. Path to the PEM format SSL client key to use when talking to the server. .. data:: LRO_SSLCACERT *String or None*. Path to a file containing the list of PEM format trusted CA certificates. .. data:: LRO_IPRESOLVE *Integer or None* Sets kind of IP addresses to use when resolving host names. Could be one of: :ref:`ipresolve-type-label` .. data:: LRO_ALLOWEDMIRRORFAILURES *Integer or None* If all transfers from a mirror failed (no successful transfer from the mirror exists) and the number of failed downloads is higher or equal to this value the mirror will be skipped (ignored) for all next downloads. **Note:** Number of failed transfers for a single mirror can outreach this number! For example, if you set this value to 1 but you allow 3 parallel downloads it is possible that all three downloads start from the mirror, before any of them can fail. Then, if all three transfers fail, the number of failures for the mirror will be 3, even if this option was set to 1. .. data:: LRO_ADAPTIVEMIRRORSORTING *Integer or None* If enabled, internal list of mirrors for each handle is re-sorted after each finished transfer. The the sorting is based on mirror error rate etc. .. data:: LRO_GNUPGHOMEDIR *String or None* set own GNUPG configuration directory (a dir with keyring). .. data:: LRO_FASTESTMIRRORTIMEOUT *Float of None* Max length of fastest mirror measurement in seconds. Default value is 2.0sec. .. data:: LRO_HTTPHEADER *List of strings or None* List of strings that represent http headers. Each header has format "header_name: content". If you add a header with no content as in 'Accept:' (no data on the right side of the colon), the internally used header will get disabled. With this option you can add new headers, replace internal headers and remove internal headers. To add a header with no content (nothing to the right side of the colon), use the form 'MyHeader;' (note the ending semicolon). Note: Headers must not be CRLF-terminated! .. data:: LRO_OFFLINE *Boolean* Make the handle work only locally, all remote URLs are ignored. Remote mirrorlists/metalinks (if they are specified) are ignored. Fastest mirror check (if enabled) is skiped. .. data:: LRO_HTTPAUTHMETHODS *Long (bitmask)* Bitmask which tell Librepo which auth metods to use. See: :ref:`auth-methods-label` .. data:: LRO_PROXYAUTHMETHODS *Long (bitmask)* Bitmask which tell Librepo which auth metods to use for proxy authentication. See: :ref:`auth-methods-label` .. data:: LRO_FTPUSEEPSV *Boolean* Enable/Disable EPSV (Extended Passive mode) for FTP. .. data:: LRO_PRESERVETIME *Boolean* If enabled, librepo will try to keep timestamps of the downloaded files in sync with that on the remote side. .. _handle-info-options-label: :class:`~.Handle` info options ------------------------------ LRI (aka LibRepo Information) constants are used to get information from :class:`.Handle` via :meth:`~.Handle.getinfo` method. .. data:: LRI_UPDATE .. data:: LRI_URLS .. data:: LRI_MIRRORLIST .. data:: LRI_MIRRORLISTURL .. data:: LRI_METALINKURL .. data:: LRI_LOCAL .. data:: LRI_PROGRESSCB .. data:: LRI_PROGRESSDATA .. data:: LRI_DESTDIR .. data:: LRI_REPOTYPE .. data:: LRI_USERAGENT .. data:: LRI_YUMDLIST .. data:: LRI_YUMSLIST .. data:: LRI_RPMMDDLIST .. data:: LRI_YUMBLIST .. data:: LRI_RPMMDBLIST .. data:: LRI_FETCHMIRRORS .. data:: LRI_MAXMIRRORTRIES .. data:: LRI_VARSUB .. data:: LRI_MIRRORS .. data:: LRI_METALINK .. data:: LRI_FASTESTMIRROR .. data:: LRI_FASTESTMIRRORCACHE .. data:: LRI_FASTESTMIRRORMAXAGE .. data:: LRI_HMFCB .. data:: LRI_SSLVERIFYPEER .. data:: LRI_SSLVERIFYHOST .. data:: LRI_SSLCLIENTCERT .. data:: LRI_SSLCLIENTKEY .. data:: LRI_SSLCACERT .. data:: LRI_IPRESOLVE .. data:: LRI_ALLOWEDMIRRORFAILURES .. data:: LRI_ADAPTIVEMIRRORSORTING .. data:: LRI_GNUPGHOMEDIR .. data:: LRI_FASTESTMIRRORTIMEOUT .. data:: LRI_HTTPHEADER .. data:: LRI_OFFLINE .. data:: LRI_HTTPAUTHMETHODS .. data:: LRI_PROXYAUTHMETHODS .. data:: LRI_FTPUSEEPSV .. _proxy-type-label: Proxy type constants -------------------- .. data:: PROXY_HTTP (LR_PROXY_HTTP) .. data:: PROXY_HTTP_1_0 (LR_PROXY_HTTP_1_0) .. data:: PROXY_SOCKS4 (LR_PROXY_SOCKS4) .. data:: PROXY_SOCKS5 (LR_PROXY_SOCKS5) .. data:: PROXY_SOCKS4A (LR_PROXY_SOCKS4A) .. data:: PROXY_SOCKS5_HOSTNAME (LR_PROXY_SOCKS5_HOSTNAME) .. _ipresolve-type-label: Supported IP resolving ---------------------- .. data:: IPRESOLVE_WHATEVER Default value, resolves addresses to all IP versions that your system allows. .. data:: IPRESOLVE_V4 Resolve to IPv4 addresses. .. data:: IPRESOLVE_V6 Resolve to IPv6 addresses. .. _repotype-constants-label: Repo type constants ------------------- .. data:: RPMMDREPO (LR_RPMMDREPO) Classical repository in repo-md format with ``repodata/`` directory. .. data:: YUMREPO (LR_YUMREPO) See RPMMDREPO .. data:: SUSEREPO (LR_SUSEREPO) YaST2 repository (http://en.opensuse.org/openSUSE:Standards_YaST2_Repository_Metadata_content). .. note:: This option is not supported yet! .. data:: DEBREPO (LR_DEBREPO) Debian repository .. note:: This option is not supported yet! .. _predefined-yumdlists-label: Predefined yumdlist lists ------------------------- .. data:: RPMMD_FULL (YUM_FULL, LR_YUM_FULL) Download all repodata files .. data:: RPMMD_REPOMDONLY (YUM_REPOMDONLY, LR_YUM_REPOMDONLY) Download only repomd.xml file .. data:: RPMMD_BASEXML (YUM_BASEXML, LR_YUM_BASEXML) Download only primary.xml, filelists.xml and other.xml .. data:: RPMMD_BASEDB (YUM_BASEDB, LR_YUM_BASEDB) Download only primary, filelists and other databases. .. data:: RPMMD_HAWKEY (YUM_HAWKEY, LR_YUM_HAWKEY) Download only files used by Hawkey (https://github.com/akozumpl/hawkey/). (primary, filelists, prestodelta) .. _auth-methods-label: Auth methods ------------ Supported auth methods for :data:`~.LRO_HTTPAUTHMETHODS` and :data:`~.LRO_PROXYAUTHMETHODS` options. .. data:: LR_AUTH_NONE No auth method enabled. .. data:: LR_AUTH_BASIC HTTP Basic authentication (Default). .. data:: LR_AUTH_DIGEST HTTP Digest authentication. .. data:: LR_AUTH_NEGOTIATE HTTP Negotiate (SPNEGO) authentication. .. data:: LR_AUTH_NTLM HTTP NTLM authentication. .. data:: LR_AUTH_DIGEST_IE HTTP Digest authentication with an IE flavor. .. data:: LR_AUTH_NTLM_WB NTLM delegating to winbind helper. .. data:: LR_AUTH_ONLY This is a meta symbol. OR this value together with a single specific auth value to force libcurl to probe for un-restricted auth and if not, only that single auth algorithm is acceptable. .. data:: LR_AUTH_ANY All suitable methods. .. _fastestmirror-stages-constants-label: Fastest mirror stages --------------------- Values used by fastest mirror callback (:data:`~.LRO_FASTESTMIRRORCB`): .. data:: FMSTAGE_INIT (0) Fastest mirror detection just started. *data* is None. .. data:: FMSTAGE_CACHELOADING (1) Cache file is specified. *data* is path to the cache file. .. data:: FMSTAGE_CACHELOADINGSTATUS (2) Cache loading finished. If successful, *data* is None, otherwise *data* is string with error message. .. data:: FMSTAGE_DETECTION (3) Detection in progress. *data* is number of mirrors that will be pinged. If all times were loaded from cache, this stage is skiped. .. data:: FMSTAGE_FINISHING (4) Detection is done, sorting mirrors, updating cache, etc. *data* is None. .. data:: FMSTAGE_STATUS (5) The very last invocation of fastest mirror callback. If fastest mirror detection was successful *data*, otherwise *data* contain string with error message. .. _error-codes-label: Error codes ----------- LibRepo Error codes. .. data:: LRE_OK (0) Everything is ok. .. data:: LRE_BADFUNCARG (1) Bad function argument(s). .. data:: LRE_BADOPTARG (2) Bad argument for the option in :meth:`~.Handle.setopt`. .. data:: LRE_UNKNOWNOPT (3) Unknown option. .. data:: LRE_CURLSETOPT (4) cURL doesn't know an option used by librepo. Probably too old cURL version is used. .. data:: LRE_ALREADYUSEDRESULT (5) :class:`.Result` object is not "clean" (it has been already used). .. data:: LRE_INCOMPLETERESULT (6) :class:`.Result` object doesn't contain all what is needed. .. data:: LRE_CURLDUP (7) Cannot duplicate cURL handle. No free memory? .. data:: LRE_CURL (8) A cURL error. .. data:: LRE_CURLM (9) A multi cURL handle error. .. data:: LRE_BADSTATUS (10) Error HTTP or FTP status code. .. data:: LRE_TEMPORARYERR (11) An error that should be temporary (e.g. HTTP status codes 500, 502-504, ..) .. data:: LRE_NOTLOCAL (12) URL is not a local address. E.g. in case when :data:`~.LRO_UPDATE` option is ``True`` and URL is a remote address. .. data:: LRE_CANNOTCREATEDIR (13) Cannot create directory for downloaded data. Bad permission? .. data:: LRE_IO (14) Input/Output error. .. data:: LRE_MLBAD (15) Bad mirrorlist or metalink file. E.g. metalink doesn't contain reference to target file (repomd.xml), mirrorlist is empty, .. .. data:: LRE_MLXML (16) Cannot parse metalink xml. Non-valid metalink file? E.g. (metalink doesn't contain needed file, mirrorlist doesn't contain urls, ..) .. data:: LRE_BADCHECKSUM (17) Bad checksum. .. data:: LRE_REPOMDXML (18) Cannot parse repomd.xml file. Non-valid repomd.xml file? .. data:: LRE_NOURL (19) No usable URL found. E.g. bad links or no links in metalink. .. data:: LRE_CANNOTCREATETMP (20) Cannot create temporary directory. .. data:: LRE_UNKNOWNCHECKSUM (21) Unknown type of checksum is needed for verification of one or more files. .. data:: LRE_BADURL (22) Bad URL specified. .. data:: LRE_GPGNOTSUPPORTED (23) OpenPGP protocol is not supported. .. data:: LRE_GPGERROR (24) GPG error. .. data:: LRE_BADGPG (25) Bad GPG signature. .. data:: LRE_INCOMPLETEREPO (26) Repository metadata are not complete. .. data:: LRE_INTERRUPTED (27) Download was interrupted by SIGTERM signal. .. data:: LRE_SIGACTION (28) Sigaction system call failed. .. data:: LRE_ALREADYDOWNLOADED (29) The file is already downloaded and its checksum matches. .. data:: LRE_UNFINISHED (30) Download wasn't (or cannot be) finished. .. data:: LRE_SELECT (31) select() call failed. .. data:: LRE_OPENSSL (32) OpenSSL library related error. .. data:: LRE_MEMORY (33) Cannot allocate more memory. .. data:: LRE_XMLPARSER (34) XML parser error. .. data:: LRE_CBINTERRUPTED (35) Interrupted by user cb. .. data:: LRE_UNKNOWNERROR An unknown error. .. _result-options-label: :class:`~.Result` options ------------------------- LibRepo Result options for use in :meth:`~.Result.getinfo` method. .. data:: LRR_YUM_REPO (deprecated - use LRR_RPMMD_REPO instead) Returns a flat dictionary with local paths to downloaded/localised rpmmd repository and basic repository's information.:: {'repomd': u'/tmp/librepotest-jPMmX5/repodata/repomd.xml', 'url': u'http://127.0.0.1:5000/yum/static/01/' 'destdir': u'/tmp/librepotest-jPMmX5', 'metalink': None, 'mirrorlist': None, 'signature': None, 'primary': u'/tmp/librepotest-jPMmX5/repodata/4543ad62e4d86337cd1949346f9aec976b847b58-primary.xml.gz', 'primary_db': u'/tmp/librepotest-jPMmX5/repodata/735cd6294df08bdf28e2ba113915ca05a151118e-primary.sqlite.bz2', 'filelists': u'/tmp/librepotest-jPMmX5/repodata/aeca08fccd3c1ab831e1df1a62711a44ba1922c9-filelists.xml.gz', 'filelists_db': u'/tmp/librepotest-jPMmX5/repodata/4034dcea76c94d3f7a9616779539a4ea8cac288f-filelists.sqlite.bz2', 'other': u'/tmp/librepotest-jPMmX5/repodata/a8977cdaa0b14321d9acfab81ce8a85e869eee32-other.xml.gz', 'other_db': u'/tmp/librepotest-jPMmX5/repodata/fd96942c919628895187778633001cff61e872b8-other.sqlite.bz2', } .. data:: LRR_RPMMD_REPO Returns a dict with local paths to downloaded/localised rpmmd repository and basic repository's information.:: {'repomd': u'/tmp/librepotest-jPMmX5/repodata/repomd.xml', 'url': u'http://127.0.0.1:5000/yum/static/01/' 'destdir': u'/tmp/librepotest-jPMmX5', 'metalink': None, 'mirrorlist': None, 'signature': None, 'paths': {'primary': u'/tmp/librepotest-jPMmX5/repodata/4543ad62e4d86337cd1949346f9aec976b847b58-primary.xml.gz', 'primary_db': u'/tmp/librepotest-jPMmX5/repodata/735cd6294df08bdf28e2ba113915ca05a151118e-primary.sqlite.bz2', 'filelists': u'/tmp/librepotest-jPMmX5/repodata/aeca08fccd3c1ab831e1df1a62711a44ba1922c9-filelists.xml.gz', 'filelists_db': u'/tmp/librepotest-jPMmX5/repodata/4034dcea76c94d3f7a9616779539a4ea8cac288f-filelists.sqlite.bz2', 'other': u'/tmp/librepotest-jPMmX5/repodata/a8977cdaa0b14321d9acfab81ce8a85e869eee32-other.xml.gz', 'other_db': u'/tmp/librepotest-jPMmX5/repodata/fd96942c919628895187778633001cff61e872b8-other.sqlite.bz2'} } .. data:: LRR_YUM_REPOMD (deprecated - use LRR_RPMMD_REPOMD instead) Returns a flat dict representing a repomd.xml file of downloaded rpmmd repository.:: {'revision': u'1347459931', 'repo_tags': [], 'content_tags': [], 'distro_tags': [], 'primary': {'checksum': u'4543ad62e4d86337cd1949346f9aec976b847b58', 'checksum_open': u'68457ceb8e20bda004d46e0a4dfa4a69ce71db48', 'checksum_open_type': u'sha1', 'checksum_type': u'sha1', 'db_version': 0L, 'location_href': u'repodata/4543ad62e4d86337cd1949346f9aec976b847b58-primary.xml.gz', 'size': 936L, 'size_open': 3385L, 'timestamp': 1347459930L}, 'primary_db': {'checksum': u'735cd6294df08bdf28e2ba113915ca05a151118e', 'checksum_open': u'ba636386312e1b597fc4feb182d04c059b2a77d5', 'checksum_open_type': u'sha1', 'checksum_type': u'sha1', 'db_version': 10L, 'location_href': u'repodata/735cd6294df08bdf28e2ba113915ca05a151118e-primary.sqlite.bz2', 'size': 2603L, 'size_open': 23552L, 'timestamp': 1347459931L}, 'filelists': {'checksum': u'aeca08fccd3c1ab831e1df1a62711a44ba1922c9', 'checksum_open': u'52d30ae3162ca863c63c345ffdb7f0e10c1414a5', 'checksum_open_type': u'sha1', 'checksum_type': u'sha1', 'db_version': 0L, 'location_href': u'repodata/aeca08fccd3c1ab831e1df1a62711a44ba1922c9-filelists.xml.gz', 'size': 43310L, 'size_open': 735088L, 'timestamp': 1347459930L}, 'filelists_db': {'checksum': u'4034dcea76c94d3f7a9616779539a4ea8cac288f', 'checksum_open': u'949c6b7b605b2bc66852630c841a5003603ca5b2', 'checksum_open_type': u'sha1', 'checksum_type': u'sha1', 'db_version': 10L, 'location_href': u'repodata/4034dcea76c94d3f7a9616779539a4ea8cac288f-filelists.sqlite.bz2', 'size': 22575L, 'size_open': 201728L, 'timestamp': 1347459931L}, 'other': {'checksum': u'a8977cdaa0b14321d9acfab81ce8a85e869eee32', 'checksum_open': u'4b5b8874fb233a626b03b3260a1aa08dce90e81a', 'checksum_open_type': u'sha1', 'checksum_type': u'sha1', 'db_version': 0L, 'location_href': u'repodata/a8977cdaa0b14321d9acfab81ce8a85e869eee32-other.xml.gz', 'size': 807L, 'size_open': 1910L, 'timestamp': 1347459930L}, 'other_db': {'checksum': u'fd96942c919628895187778633001cff61e872b8', 'checksum_open': u'c5262f62b6b3360722b9b2fb5d0a9335d0a51112', 'checksum_open_type': u'sha1', 'checksum_type': u'sha1', 'db_version': 10L, 'location_href': u'repodata/fd96942c919628895187778633001cff61e872b8-other.sqlite.bz2', 'size': 1407L, 'size_open': 8192L, 'timestamp': 1347459931L}, } .. data:: LRR_RPMMD_REPOMD Returns a dict representing a repomd.xml file of downloaded rpmmd repository.:: {'revision': u'1347459931', 'repo_tags': [], 'content_tags': [], 'distro_tags': [], 'records': {'primary': {'checksum': u'4543ad62e4d86337cd1949346f9aec976b847b58', 'checksum_open': u'68457ceb8e20bda004d46e0a4dfa4a69ce71db48', 'checksum_open_type': u'sha1', 'checksum_type': u'sha1', 'db_version': 0L, 'location_href': u'repodata/4543ad62e4d86337cd1949346f9aec976b847b58-primary.xml.gz', 'size': 936L, 'size_open': 3385L, 'timestamp': 1347459930L}, 'primary_db': {'checksum': u'735cd6294df08bdf28e2ba113915ca05a151118e', 'checksum_open': u'ba636386312e1b597fc4feb182d04c059b2a77d5', 'checksum_open_type': u'sha1', 'checksum_type': u'sha1', 'db_version': 10L, 'location_href': u'repodata/735cd6294df08bdf28e2ba113915ca05a151118e-primary.sqlite.bz2', 'size': 2603L, 'size_open': 23552L, 'timestamp': 1347459931L}}, 'filelists': {'checksum': u'aeca08fccd3c1ab831e1df1a62711a44ba1922c9', 'checksum_open': u'52d30ae3162ca863c63c345ffdb7f0e10c1414a5', 'checksum_open_type': u'sha1', 'checksum_type': u'sha1', 'db_version': 0L, 'location_href': u'repodata/aeca08fccd3c1ab831e1df1a62711a44ba1922c9-filelists.xml.gz', 'size': 43310L, 'size_open': 735088L, 'timestamp': 1347459930L}, 'filelists_db': {'checksum': u'4034dcea76c94d3f7a9616779539a4ea8cac288f', 'checksum_open': u'949c6b7b605b2bc66852630c841a5003603ca5b2', 'checksum_open_type': u'sha1', 'checksum_type': u'sha1', 'db_version': 10L, 'location_href': u'repodata/4034dcea76c94d3f7a9616779539a4ea8cac288f-filelists.sqlite.bz2', 'size': 22575L, 'size_open': 201728L, 'timestamp': 1347459931L}, 'other': {'checksum': u'a8977cdaa0b14321d9acfab81ce8a85e869eee32', 'checksum_open': u'4b5b8874fb233a626b03b3260a1aa08dce90e81a', 'checksum_open_type': u'sha1', 'checksum_type': u'sha1', 'db_version': 0L, 'location_href': u'repodata/a8977cdaa0b14321d9acfab81ce8a85e869eee32-other.xml.gz', 'size': 807L, 'size_open': 1910L, 'timestamp': 1347459930L}, 'other_db': {'checksum': u'fd96942c919628895187778633001cff61e872b8', 'checksum_open': u'c5262f62b6b3360722b9b2fb5d0a9335d0a51112', 'checksum_open_type': u'sha1', 'checksum_type': u'sha1', 'db_version': 10L, 'location_href': u'repodata/fd96942c919628895187778633001cff61e872b8-other.sqlite.bz2', 'size': 1407L, 'size_open': 8192L, 'timestamp': 1347459931L}, } .. data:: LRR_RPMMD_TIMESTAMP (LRR_YUM_TIMESTAMP) Returns the highest timestamp from all records in the repomd. See: http://yum.baseurl.org/gitweb?p=yum.git;a=commitdiff;h=59d3d67f .. _endcb-statuses-label: Transfer statuses for endcb of :class:`~.PackageTarget` ------------------------------------------------------- .. data:: TRANSFER_SUCCESSFUL .. data:: TRANSFER_ALREADYEXISTS .. data:: TRANSFER_ERROR Callbacks prototypes -------------------- Some librepo's functions and classes can take a callbacks in their arguments. This section contain the callbacks prototypes and explanation of their arguments. By default all callbacks should return *None* (this is default behaviour of python function if it doesn't have a specified return statement - it returns None). But there are some callbacks that can return a specific values defined in :ref:`callbacks-return-values`. .. _callback-progresscb-label: Progress callback - progresscb ------------------------------ ``progresscb(userdata, totalsize, downloaded)`` Callback frequently called during a download. :userdata: User specified data or *None* :totalsize: Total size (in bytes) of the target (float). :downloaded: Currently downloaded size (in bytes). :returns: This callback can return values from :ref:`callbacks-return-values` .. _callback-endcb-label: End callback - endcb -------------------- ``endcb(userdata, status, msg)`` Callback called when a transfer is done either successfully or unsuccessfully. :userdata: User specified data or *None* :status: :ref:`endcb-statuses-label` :msg: String with error message or *None* :returns: This callback can return values from :ref:`callbacks-return-values` .. _callback-mirrorfailurecb-label: Mirror Failure Callback - mirrorfailurecb ----------------------------------------- ``mirrorfailurecb(userdata, msg, url)`` Callback called when a transfer failed. :userdata: User specified data or *None* :msg: String with error message :url: String with mirror URL :returns: This callback can return values from :ref:`callbacks-return-values` .. _callback-fastestmirrorcb-label: Fastestmirror callback - fastestmirrorcb ---------------------------------------- ``fastestmirrorcb(userdata, stage, data)`` :userdata: User specified data or *None* :stage: :ref:`fastestmirror-stages-constants-label` :data: Content of *data* is different for different stages. See :ref:`fastestmirror-stages-constants-label` :returns: This callback must return *None* each other value will be ignored. .. _callback-handlemirrorfailurecb-label: Handle Mirror Failure Callback - hmfcb -------------------------------------- ``hmfcb(userdata, msg, url, metadata)`` Callback called when a download of a metadata during :meth:`~.Handle.perform()` fails. :userdata: User specified data or *None* :msg: String with error message :url: String with mirror URL :metadata: String with metadata name ("primary", "filelists", ...) :returns: This callback can return values from :ref:`callbacks-return-values` .. _callbacks-return-values: Callbacks return values ----------------------- Each callback can safely return ``None`` (this is implicit return value when no return statement is defined). ``return None`` is equivalent for ``return librepo.CB_OK``. .. data:: CB_OK Everything is OK .. data:: CB_ABORT Abort just the current transfer. Note: If failfast option for the downloading is enabled, the whole downloading will be aborted. .. data:: CB_ERROR Abort the whole downloading (all transfers). (Note: This code is automatically internally returned when an exception is raised in the callback.) .. _checksum-constants-label: Checksum (hash) type constants ------------------------------ .. data:: MD5 (CHECKSUM_MD5) .. data:: SHA1 (CHECKSUM_SHA1) .. data:: SHA224 (CHECKSUM_SHA224) .. data:: SHA256 (CHECKSUM_SHA256) .. data:: SHA384 (CHECKSUM_SHA384) .. data:: SHA512 (CHECKSUM_SHA512) """ import sys import librepo._librepo current_module = sys.modules[__name__] LibrepoException = _librepo.LibrepoException ATTR_TO_LRO = {} ATTR_TO_LRI = {} ATTR_TO_LRR = {} _CHECKSUM_STR_TO_VAL_MAP = {} # Create local aliases for contants from _librepo C module for attr in dir(_librepo): if not attr.isupper(): # Only constants should be imported automatically continue # Create local alias val = getattr(_librepo, attr) setattr(current_module, attr, val) if attr.endswith("_SENTINEL"): # Ignore sentinel values continue if attr.startswith("LR_"): setattr(current_module, attr[3:], val) if attr.startswith("LRO_"): ATTR_TO_LRO[attr.lower()[4:]] = val if attr.startswith("LRI_"): ATTR_TO_LRI[attr.lower()[4:]] = val if attr.startswith("LRR_"): ATTR_TO_LRR[attr.lower()[4:]] = val if attr.startswith("LR_CHECKSUM_"): setattr(current_module, attr[12:], val) _CHECKSUM_STR_TO_VAL_MAP[attr[12:].lower()] = val RPMMD_FULL = YUM_FULL = LR_YUM_FULL = None RPMMD_REPOMDONLY= YUM_REPOMDONLY = LR_YUM_REPOMDONLY = [None] RPMMD_BASEXML = YUM_BASEXML = LR_YUM_BASEXML = ["primary", "filelists", "other", None] RPMMD_BASEDB = YUM_BASEDB = LR_YUM_BASEDB = ["primary_db", "filelists_db", "other_db", None] RPMMD_HAWKEY = YUM_HAWKEY = LR_YUM_HAWKEY = ["primary", "filelists", "prestodelta", None] def checksum_str_to_type(name): """ Convert string with name of hash function to numeric value that represents the hash in createrepo_c. :param name: Checksum name (e.g. "sha256", "sha512", ...) :returns: Integer value (from :ref:`checksum-constants-label`) """ name = name.lower() return _CHECKSUM_STR_TO_VAL_MAP.get(name, CHECKSUM_UNKNOWN) class MetadataTarget(_librepo.MetadataTarget): def __init__(self, handle=None, cbdata=None, progresscb=None, mirrorfailurecb=None, endcb=None, gnupghomedir=None): _librepo.MetadataTarget.__init__(self, handle, cbdata, progresscb, mirrorfailurecb, endcb, gnupghomedir) class PackageTarget(_librepo.PackageTarget): """ Represent a single package that will be downloaded by :func:`~librepo.download_packages`. """ def __init__(self, relative_url, dest=None, checksum_type=CHECKSUM_UNKNOWN, checksum=None, expectedsize=0, base_url=None, resume=False, progresscb=None, cbdata=None, handle=None, endcb=None, mirrorfailurecb=None, byterangestart=0, byterangeend=0): """ :param relative_url: Target URL. If *handle* or *base_url* specified, the *url* can be (and logically should be) only a relative part of path. :param dest: Destination filename or directory (file basename will be derived from the relative_url). If *None* current working directory will be used. :param checksum_type: :ref:`checksum-constants-label` :param checksum: Expected checksum value. :param expectedsize: Expected size of the target. If server reports different size, then download won't be performed. :param base_url: Base part of URL :param resume: If True then downloader will try to resume download if the destination file already exists. If the file doesn't exist yet, it will be downloaded. :param progresscb: :ref:`callback-progresscb-label` :param cbdata: User data for the callback. :param handle: :class:`~librepo.Handle` :param endcb: :ref:`callback-endcb-label` :param mirrorfailurecb: See :ref:`callback-mirrorfailurecb-label` :param byterangestart: Start downloading from the specified byte. :param byterangeend: Stop downloading at the specified byte. *Note: If the byterangeend is less or equal to byterangestart, then it is ignored!* """ _librepo.PackageTarget.__init__(self, handle, relative_url, dest, checksum_type, checksum, expectedsize, base_url, resume, progresscb, cbdata, endcb, mirrorfailurecb, byterangestart, byterangeend) class Handle(_librepo.Handle): """Librepo handle class. Handle hold information about a repository and configuration for downloading from the repository. **Attributes:** .. attribute:: update: See: :data:`.LRO_UPDATE` .. attribute:: urls: See: :data:`.LRO_URLS` .. attribute:: mirrorlist: See: :data:`.LRO_MIRRORLIST` .. attribute:: mirrorlisturl: See: :data:`.LRO_MIRRORLISTURL` .. attribute:: metalinkurl: See: :data:`.LRO_METALINKURL` .. attribute:: local: See: :data:`.LRO_LOCAL` .. attribute:: httpauth: See: :data:`.LRO_HTTPAUTH` .. attribute:: userpwd: See: :data:`.LRO_USERPWD` .. attribute:: proxy: See: :data:`.LRO_PROXY` .. attribute:: proxyport: See: :data:`.LRO_PROXYPORT` .. attribute:: proxytype: See: :data:`.LRO_PROXYTYPE` .. attribute:: proxyauth: See: :data:`.LRO_PROXYAUTH` .. attribute:: proxyuserpwd: See: :data:`.LRO_PROXYUSERPWD` .. attribute:: progresscb: See: :data:`.LRO_PROGRESSCB` .. attribute:: progressdata: See: :data:`.LRO_PROGRESSDATA` .. attribute:: maxspeed: See: :data:`.LRO_MAXSPEED` .. attribute:: destdir: See: :data:`.LRO_DESTDIR` .. attribute:: repotype: See: :data:`.LRO_REPOTYPE` .. attribute:: connecttimeout: See: :data:`.LRO_CONNECTTIMEOUT` .. attribute:: ignoremissing: See: :data:`.LRO_IGNOREMISSING` .. attribute:: interruptible: See: :data:`.LRO_INTERRUPTIBLE` .. attribute:: useragent: See: :data:`.LRO_USERAGENT` .. attribute:: fetchmirrors: See: :data:`.LRO_FETCHMIRRORS` .. attribute:: maxmirrortries: See: :data:`.LRO_MAXMIRRORTRIES` .. attribute:: maxparalleldownloads: See: :data:`.LRO_MAXPARALLELDOWNLOADS` .. attribute:: maxdownloadspermirror: See: :data:`.LRO_MAXDOWNLOADSPERMIRROR` .. attribute:: varsub: See: :data:`.LRO_VARSUB` .. attribute:: fastestmirror: See: :data:`.LRO_FASTESTMIRROR` .. attribute:: fastestmirrorcache: See: :data:`.LRO_FASTESTMIRRORCACHE` .. attribute:: fastestmirrormaxage: See: :data:`.LRO_FASTESTMIRRORMAXAGE` .. attribute:: fastestmirrorcb: See: :data:`.LRO_FASTESTMIRRORCB` .. attribute:: fastestmirrordata: See: :data:`.LRO_FASTESTMIRRORDATA` .. attribute:: lowspeedtime: See: :data:`.LRO_LOWSPEEDTIME` .. attribute:: lowspeedlimit: See: :data:`.LRO_LOWSPEEDLIMIT` .. attribute:: gpgcheck: See: :data:`.LRO_GPGCHECK` .. attribute:: checksum: See: :data:`.LRO_CHECKSUM` .. attribute:: rpmmddlist: See: :data:`.LRO_RPMMDDLIST` .. attribute:: yumdlist: See: :data:`.LRO_YUMDLIST` .. attribute:: yumslist: See: :data:`.LRO_YUMSLIST` .. attribute:: rpmmdblist: See: :data:`.LRO_RPMMDBLIST` .. attribute:: yumblist: See: :data:`.LRO_YUMBLIST` .. attribute:: hmfcb: See: :data:`.LRO_HMFCB` .. attribute:: sslverifypeer: See :data:`.LRO_SSLVERIFYPEER` .. attribute:: sslverifyhost: See :data:`.LRO_SSLVERIFYHOST` .. attribute:: sslclientcert: See :data:`.LRO_SSLCLIENTCERT` .. attribute:: sslclientkey: See :data:`.LRO_SSLCLIENTKEY` .. attribute:: sslcacert: See :data:`.LRO_SSLCACERT` .. attribute:: ipresolve: See :data:`.LRO_IPRESOLVE` .. attribute:: allowedmirrorfailures: See :data:`.LRO_ALLOWEDMIRRORFAILURES` .. attribute:: adaptivemirrorsorting: See :data:`.LRO_ADAPTIVEMIRRORSORTING` .. attribute:: gnupghomedir: See :data:`.LRO_GNUPGHOMEDIR` .. attribute:: fastestmirrortimeout: See :data:`.LRO_FASTESTMIRRORTIMEOUT` .. attribute:: httpheader: See :data:`.LRO_HTTPHEADER` .. attribute:: offline: See :data:`.LRO_OFFLINE` .. attribute:: httpauthmethods See :data:`.LRO_HTTPAUTHMETHODS` .. attribute:: proxyauthmethods See :data:`.LRO_PROXYAUTHMETHODS` .. attribute:: ftpuseepsv See :data:`.LRO_FTPUSEEPSV` .. attribute:: preservetime See :data:`.LRO_PRESERVETIME` """ def setopt(self, option, val): """Set option to :class:`.Handle` directly. :param option: One of: :ref:`handle-options-label` :returns: *None* Example:: # The command: h.setopt(librepo.LRO_URLS, ["http://ftp.linux.ncsu.edu/pub/fedora/linux/releases/17/Everything/i386/os/"]) # is equivalent to: h.urls(["http://ftp.linux.ncsu.edu/pub/fedora/linux/releases/17/Everything/i386/os/"]) """ if (option == LRO_URLS and (not isinstance(val, list) and val is not None)): import warnings warnings.warn("Using string value for LRO_URLS is deprecated, " \ "use list of strings instead", DeprecationWarning) val = [val] _librepo.Handle.setopt(self, option, val) def __setattr__(self, attr, val): if attr not in ATTR_TO_LRO and attr in ATTR_TO_LRI: raise AttributeError("Set of attribute '%s' is not supported" % attr) elif attr not in ATTR_TO_LRO: raise AttributeError("'%s' object has no attribute '%s'" % \ (self.__class__.__name__, attr)) self.setopt(ATTR_TO_LRO[attr], val) def __getattr__(self, attr): if attr not in ATTR_TO_LRI and attr in ATTR_TO_LRO: raise AttributeError("Read of attribute '%s' is not supported" % attr) elif attr not in ATTR_TO_LRI: raise AttributeError("'%s' object has no attribute '%s'" % \ (self.__class__.__name__, attr)) return _librepo.Handle.getinfo(self, ATTR_TO_LRI[attr]) def getinfo(self, option): """Get information from :class:`.Handle`. :param option: One of :ref:`handle-info-options-label` :returns: Value for the specified option or *None* if the option is not set. """ return _librepo.Handle.getinfo(self, option) def download(self, url, dest=None, checksum_type=CHECKSUM_UNKNOWN, checksum=None, expectedsize=0, base_url=None, resume=0): """ **This method is deprecated** - Use :func:`~librepo.download_packages` instead. Download package from the repository specified in the :Class:`~librepo.Handle`. If *base_url* is specified, urls and mirrors specified in the Handle are ignored. Note: If resume is True and checksum_type and checksum are specified and downloaded package already exists, then checksum of the existing package is checked. If checksum matches, then no downloading is done and exception with LRE_ALREADYDOWNLOADED return code is raised. :param url: Relative path to the package in the repository. :param dest: Destination for package. Could be absolute/relative path to directory or filename. :param checksum_type: :ref:`checksum-constants-label`. :param checksum: Checksum value. :param expectedsize: Expected size of the file. If server reports different size, then no download is preformed. :param base_url: Instead of repositories specified in ``Handle`` use repository on this url. :param resume: ``True`` enables resume. Resume means that if local destination file exists, just try to resume download, if not or resume download failed than download whole file again. Example:: h = librepo.Handle() h.setopt(librepo.LRO_URLS, ["http://ftp.linux.ncsu.edu/pub/fedora/linux/releases/17/Everything/i386/os/"]) h.setopt(librepo.LRO_REPOTYPE, librepo.LR_YUMREPO) h.download("Packages/s/sl-3.03-12.fc17.i686.rpm", checksum="0ec8535d0fc00b497d8aef491c3f8b3955f2d84846325ee44851d9de8a36d12c", checksum_type=librepo.CHECKSUM_SHA256) .. note:: If checksum check is disabled in the current ``Handle``, then checksum is NOT checked even if *checksum* and *checksum_type* params are specified! """ if isinstance(checksum_type, str): checksum_type = checksum_str_to_type(checksum_type) self.download_package(url, dest, checksum_type, checksum, expectedsize, base_url, resume) def perform(self, result=None): """ Perform the specified action - download/locate a repository. :param result: :Class:`~librepo.Result` object or *None* :returns: :Class:`~librepo.Result` object that was passed by *result* parameter or the new one if the parameter was not specified or was *None*. """ if result is None: result = Result() _librepo.Handle.perform(self, result) return result def new_packagetarget(self, relative_url, **kwargs): """ Shortcut for creating a new :Class:`~librepo.PackageTarget` objects. Targets created by this way have automatically setted handle to the current :Class:`~librepo.Handle` object. :param relative_url: Relative par of target (package) URL :returns: New :Class:`~librepo.PackageTarget` """ return PackageTarget(relative_url, handle=self, **kwargs) class Result(_librepo.Result): """Librepo result class This class holds information about a downloaded/localised repository. **Attributes:** .. attribute:: rpmmd_repo See: :data:`.LRR_RPMMD_REPO` .. attribute:: yum_repo See: :data:`.LRR_YUM_REPO` .. attribute:: rpmmd_repomd See: :data:`.LRR_RPMMD_REPOMD` .. attribute:: yum_repomd See: :data:`.LRR_YUM_REPOMD` .. attribute:: rpmmd_timestamp See: :data:`.LRR_RPMMD_TIMESTAMP` .. attribute:: yum_timestamp See: :data:`.LRR_YUM_TIMESTAMP` """ def getinfo(self, option): """Returns information about a downloaded/localised repository. *option* could be one of: :ref:`result-options-label` """ return _librepo.Result.getinfo(self, option) def __getattr__(self, attr): if attr not in ATTR_TO_LRR: raise AttributeError("'%s' object has no attribute '%s'" % \ (self.__class__.__name__, attr)) return self.getinfo(ATTR_TO_LRR[attr]) # Functions def download_metadata(list): """ Download metadata. *list* is a list of :class:`~librepo.MetadataTarget` objects. Exception is raised only if a nonrecoverable error related to the function itself is meet (Errors related to individual downloads are reported via corresponding MetadataTarget objects) :param list: List of :class:`~.librepo.MetadataTarget` objects. :returns: *None* """ return _librepo.download_metadata(list) def download_packages(list, failfast=False): """ Download list of packages. *list* is a list of :class:`~librepo.PackageTarget` objects. If the *failfast* is True, then whole downloading is stopped immediately when any of download fails (and exception is raised). If the failfast is False, then this function returns after all downloads finish (no matter if successfully or unsuccessfully) and exception is raised only if a nonrecoverable error related to the function itself is meet (Errors related to individual downloads are reported via corresponding PackageTarget objects) :param list: List of :class:`~.librepo.PackageTarget` objects. :param failfast: If *True*, stop whole downloading immediately when any of downloads fails. If *False*, ignore failed download(s) and continue with other downloads. :returns: *None* """ return _librepo.download_packages(list, failfast) def download_url(url, fd, handle=None): """ Download specified URL and write it content to opened file descriptor. :param url: Target URL :param fd: Opened file descriptor (To get a file descriptor use for example **os.open()**. :param handle: :Class:`~librepo.Handle` object or *None* :returns: *None* """ return _librepo.download_url(handle, url, fd) def yum_repomd_get_age(result_object): """ Get the highest timestamp of the repo's repomd.xml. :param result_object: Used (filled) :Class:`~librepo.Result` :returns: The highest timestamp from repomd (Float) or *0.0* on error. """ return _librepo.yum_repomd_get_age(result_object) def set_debug_log_handler(log_function, user_data=None): """ ONLY FOR DEVELOPMENT (DEBUGGING) PURPOSES! (Deprecated, use :func:`~log_set_file` instead) When python debug log handler is used, the librepo is **THREAD-UNSAFE**! If used, it overrides logging set by :func:`~log_set_file` and vice versa. :param log_function: Function that will handle the debug messages. :param user_data: An data you want to be passed to the log_function during call. :returns: *None* Example:: def debug_function(msg, _): print msg librepo.set_debug_log_handler(debug_function) """ return _librepo.set_debug_log_handler(log_function, user_data) def log_set_file(filename): """Set a filename of a file where logs are going to be written. Note: Only one file could be set at a time. Note: If the LIBREPO_DEBUG environ variable is set and this function is used then the LIBREPO_DEBUG effect will be suppressed. (All debug output will be redirected to the specified file) :param filename: Filename :returns: Id of the handler """ return _librepo.log_set_file(filename) def log_remove_handler(handler_id): """Remove handler. :param handler_id: id """ return _librepo.log_remove_handler(handler_id) librepo-1.12.1/librepo/python/downloader-py.c000066400000000000000000000056421372027637700211620ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #undef NDEBUG #include #include "librepo/librepo.h" #include "packagedownloader-py.h" #include "handle-py.h" #include "packagetarget-py.h" #include "exception-py.h" #include "globalstate-py.h" // GIL Hack void BeginAllowThreads(PyThreadState **state) { assert(state); assert(*state == NULL); (*state) = PyEval_SaveThread(); } void EndAllowThreads(PyThreadState **state) { assert(state); assert(*state); PyEval_RestoreThread(*state); (*state) = NULL; } PyObject * py_download_url(G_GNUC_UNUSED PyObject *self, PyObject *args) { gboolean ret; PyObject *py_handle; LrHandle *handle = NULL; char *url; int fd; GError *tmp_err = NULL; PyThreadState *state = NULL; if (!PyArg_ParseTuple(args, "Osi:download_url", &py_handle, &url, &fd)) return NULL; if (HandleObject_Check(py_handle)) { handle = Handle_FromPyObject(py_handle); Handle_SetThreadState(py_handle, &state); } else if (py_handle != Py_None) { PyErr_SetString(PyExc_TypeError, "Only Handle or None is supported"); return NULL; } // XXX: GIL Hack int hack_rc = gil_logger_hack_begin(&state); if (hack_rc == GIL_HACK_ERROR) return NULL; BeginAllowThreads(&state); ret = lr_download_url(handle, url, fd, &tmp_err); EndAllowThreads(&state); // XXX: GIL Hack if (!gil_logger_hack_end(hack_rc)) return NULL; assert((ret && !tmp_err) || (!ret && tmp_err)); if (ret) Py_RETURN_NONE; // All fine - Return None // Error occurred if (PyErr_Occurred()) { // Python exception occurred (in a python callback probably) return NULL; } else if(tmp_err->code == LRE_INTERRUPTED) { // Interrupted by Ctr+C g_error_free(tmp_err); PyErr_SetInterrupt(); PyErr_CheckSignals(); return NULL; } else { // Return exception created from GError RETURN_ERROR(&tmp_err, -1, NULL); } } librepo-1.12.1/librepo/python/downloader-py.h000066400000000000000000000022401372027637700211560ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_DOWNLOADER_PY_H__ #define __LR_DOWNLOADER_PY_H__ #include "librepo/librepo.h" PyObject *py_download_url(PyObject *self, PyObject *args); void BeginAllowThreads(PyThreadState **state); void EndAllowThreads(PyThreadState **state); #endif librepo-1.12.1/librepo/python/exception-py.c000066400000000000000000000073701372027637700210220ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "exception-py.h" #include "typeconversion.h" PyObject *LrErr_Exception = NULL; int init_exceptions() { LrErr_Exception = PyErr_NewException("librepo.LibrepoException", NULL, NULL); if (!LrErr_Exception) return 0; Py_INCREF(LrErr_Exception); return 1; } void * return_error(GError **err, int rc, const char *format, ...) { int ret, code; va_list vl; gchar *message, *err_message, *usr_message = NULL; PyObject *exception_type, *exception_val; assert(err || rc > 0); assert(!err || *err); if (format) { // Prepare user message va_start(vl, format); ret = g_vasprintf(&usr_message, format, vl); va_end(vl); if (ret < 0) { // vasprintf failed - silently ignore this error g_free(usr_message); usr_message = NULL; } } // Select error message if (err) err_message = (*err)->message; else err_message = (char *) lr_strerror(rc); // Prepare complete message if (usr_message) message = g_strdup_printf("%s%s", usr_message, err_message); else message = g_strdup(err_message); g_free(usr_message); if (err) code = (*err)->code; else code = rc; g_clear_error(err); // Select appropriate exception type switch (code) { case LRE_IO: case LRE_CANNOTCREATEDIR: case LRE_CANNOTCREATETMP: exception_type = PyExc_IOError; break; case LRE_MEMORY: exception_type = PyExc_MemoryError; break; case LRE_BADFUNCARG: case LRE_BADOPTARG: exception_type = PyExc_ValueError; break; default: exception_type = LrErr_Exception; } PyObject *py_msg = PyStringOrNone_FromString(message); PyObject *py_strerror = PyStringOrNone_FromString(lr_strerror(code)); // Set exception if (exception_type == PyExc_IOError) { // Because of IOError exception has a special formating // It Looks like: // [Errno unknown] Cannot create output directory: 'Cannot create output directory' PyObject *py_unknown = PyStringOrNone_FromString("unknown"); exception_val = Py_BuildValue("(OOO)", py_unknown, py_msg, py_strerror); Py_DECREF(py_unknown); } else { exception_val = Py_BuildValue("(iOO)", (int) code, py_msg, py_strerror); } Py_DECREF(py_msg); Py_DECREF(py_strerror); PyErr_SetObject(exception_type, exception_val); g_free(message); return NULL; } librepo-1.12.1/librepo/python/exception-py.h000066400000000000000000000024551372027637700210260ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_EXCEPTION_PY_H__ #define __LR_EXCEPTION_PY_H__ #include "librepo/librepo.h" extern PyObject *LrErr_Exception; int init_exceptions(); #define RETURN_ERROR(...) \ do { return return_error(__VA_ARGS__); } while(0) /** Set exception and return NULL. * If err is passed, then rc param is ignored. */ void *return_error(GError **err, int rc, const char *format, ...); #endif librepo-1.12.1/librepo/python/globalstate-py.h000066400000000000000000000070331372027637700213260ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_GLOBALSTATE_PY_H__ #define __LR_GLOBALSTATE_PY_H__ #include "exception-py.h" /* XXX: GIL Hack for debug logging callback * * The librepo logging callback is global for whole module. * This callback is called from different parts of librepo module. * This callback calls a python code. * When C module wants to call a python code, it HAS TO have a GIL. * This is a problem since the Librepo python bindings * releases the GIL before calling librepo blocking functions like: * - lr_handle_perform = librepo.Handle.perform() * - lr_download_package = librepo.Handle.download() * - lr_downlodad_packages = librepo.download_packages() * When the callback is called within any of this function, * it leads to the state in which a python code is called without * holding a GIL which causes a crash. * * So when a debug log handler is used the Librepo starts to * be thread-unsafe! * * This GIL hack is designed to detect this and raise an exception. * Exception with a comprehensible description is much more better * then interpreter crash. */ extern volatile int global_logger; // Python callback for the logger is set extern volatile PyThreadState **global_state; // Thread state #define GIL_HACK_ERROR 0 // Other python thread with running librepo // already exists. Raise an error. #define GIL_HACK_MUST_CLEAR 1 #define GIL_HACK_DO_NOT_CLEAR 2 G_LOCK_EXTERN(gil_hack_lock); static int gil_logger_hack_begin(PyThreadState **state) { int ret = GIL_HACK_DO_NOT_CLEAR; G_LOCK(gil_hack_lock); if (global_logger) { if (global_state) { // Raise exception PyErr_SetString(LrErr_Exception, "Librepo is not threadsafe when python debug " "logger is used! Other thread using librepo " "was detected."); ret = GIL_HACK_ERROR; } else { global_state = (volatile PyThreadState **) state; ret = GIL_HACK_MUST_CLEAR; } } G_UNLOCK(gil_hack_lock); return ret; } static gboolean gil_logger_hack_end(int hack_begin_rc) { int ret; G_LOCK(gil_hack_lock); switch (hack_begin_rc) { case GIL_HACK_MUST_CLEAR: global_state = NULL; ret = TRUE; break; case GIL_HACK_DO_NOT_CLEAR: ret = TRUE; break; case GIL_HACK_ERROR: default: PyErr_SetString(LrErr_Exception, "GIL logger hack failed"); ret = FALSE; } G_UNLOCK(gil_hack_lock); return ret; } #endif librepo-1.12.1/librepo/python/handle-py.c000066400000000000000000001112201372027637700202450ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #undef NDEBUG #include #include "librepo/librepo.h" #include "exception-py.h" #include "handle-py.h" #include "packagetarget-py.h" #include "result-py.h" #include "typeconversion.h" #include "packagedownloader-py.h" #include "downloader-py.h" #include "globalstate-py.h" // GIL Hack typedef struct { PyObject_HEAD LrHandle *handle; /* Callbacks */ PyObject *progress_cb; PyObject *progress_cb_data; PyObject *fastestmirror_cb; PyObject *fastestmirror_cb_data; PyObject *hmf_cb; /* GIL stuff */ // See: http://docs.python.org/2/c-api/init.html#releasing-the-gil-from-extension-code PyThreadState **state; } _HandleObject; LrHandle * Handle_FromPyObject(PyObject *o) { if (!HandleObject_Check(o)) { PyErr_SetString(PyExc_TypeError, "Expected a _librepo.Handle object."); return NULL; } return ((_HandleObject *)o)->handle; } void Handle_SetThreadState(PyObject *o, PyThreadState **state) { _HandleObject *self = (_HandleObject *) o; if (!self) return; self->state = state; } static int check_HandleStatus(const _HandleObject *self) { assert(self != NULL); assert(HandleObject_Check(self)); if (self->handle == NULL) { PyErr_SetString(LrErr_Exception, "No librepo handle"); return -1; } return 0; } /* Callback stuff */ static int progress_callback(void *data, double total_to_download, double now_downloaded) { int ret = LR_CB_OK; // Assume everything will be ok _HandleObject *self; PyObject *user_data, *result; self = (_HandleObject *)data; if (!self->progress_cb) return LR_CB_OK; if (self->progress_cb_data) user_data = self->progress_cb_data; else user_data = Py_None; EndAllowThreads(self->state); result = PyObject_CallFunction(self->progress_cb, "(Odd)", user_data, total_to_download, now_downloaded); if (!result) { // Exception raised in callback leads to the abortion // of whole downloading (it is considered fatal) ret = LR_CB_ERROR; } else { if (result == Py_None) { // Assume that None means that everything is ok ret = LR_CB_OK; #if PY_MAJOR_VERSION < 3 } else if (PyInt_Check(result)) { ret = PyInt_AS_LONG(result); #endif } else if (PyLong_Check(result)) { ret = (int) PyLong_AsLong(result); } else { // It's an error if result is None neither int PyErr_SetString(PyExc_TypeError, "Progress callback must return integer number"); ret = LR_CB_ERROR; } } Py_XDECREF(result); BeginAllowThreads(self->state); return ret; } static void fastestmirror_callback(void *data, LrFastestMirrorStages stage, void *ptr) { _HandleObject *self; PyObject *user_data, *result, *pydata; self = (_HandleObject *)data; if (!self->fastestmirror_cb) return; if (self->fastestmirror_cb_data) user_data = self->fastestmirror_cb_data; else user_data = Py_None; EndAllowThreads(self->state); if (!ptr) { pydata = Py_None; } else { switch (stage) { case LR_FMSTAGE_CACHELOADING: case LR_FMSTAGE_CACHELOADINGSTATUS: case LR_FMSTAGE_STATUS: pydata = PyStringOrNone_FromString((char *) ptr); break; case LR_FMSTAGE_DETECTION: pydata = PyLong_FromLong(*((long *) ptr)); break; default: pydata = Py_None; } } result = PyObject_CallFunction(self->fastestmirror_cb, "(OlO)", user_data, (long) stage, pydata); Py_XDECREF(result); BeginAllowThreads(self->state); if (pydata != Py_None) Py_XDECREF(pydata); return; } static int hmf_callback(void *data, const char *msg, const char *url, const char *metadata) { int ret = LR_CB_OK; // Assume everything will be ok _HandleObject *self; PyObject *user_data, *result, *py_msg, *py_url, *py_metadata; self = (_HandleObject *)data; if (!self->hmf_cb) return LR_CB_OK; if (self->progress_cb_data) user_data = self->progress_cb_data; else user_data = Py_None; EndAllowThreads(self->state); py_msg = PyStringOrNone_FromString(msg); py_url = PyStringOrNone_FromString(url); py_metadata = PyStringOrNone_FromString(metadata); result = PyObject_CallFunction(self->hmf_cb, "(OOOO)", user_data, py_msg, py_url, py_metadata); Py_DECREF(py_msg); Py_DECREF(py_url); Py_DECREF(py_metadata); if (!result) { // Exception raised in callback leads to the abortion // of whole downloading (it is considered fatal) ret = LR_CB_ERROR; } else { if (result == Py_None) { // Assume that None means that everything is ok ret = LR_CB_OK; #if PY_MAJOR_VERSION < 3 } else if (PyInt_Check(result)) { ret = PyInt_AS_LONG(result); #endif } else if (PyLong_Check(result)) { ret = (int) PyLong_AsLong(result); } else { // It's an error if result is None neither int PyErr_SetString(PyExc_TypeError, "HandleMirrorFailure callback must return integer number"); ret = LR_CB_ERROR; } } Py_XDECREF(result); BeginAllowThreads(self->state); return ret; } /* Function on the type */ static PyObject * handle_new(PyTypeObject *type, G_GNUC_UNUSED PyObject *args, G_GNUC_UNUSED PyObject *kwds) { _HandleObject *self = (_HandleObject *)type->tp_alloc(type, 0); if (self) { self->handle = NULL; self->progress_cb = NULL; self->progress_cb_data = NULL; self->fastestmirror_cb = NULL; self->fastestmirror_cb_data = NULL; self->hmf_cb = NULL; self->state = NULL; } return (PyObject *)self; } static int handle_init(_HandleObject *self, PyObject *args, PyObject *kwds) { char *kwlist[] = {NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|", kwlist)) return -1; self->handle = lr_handle_init(); if (self->handle == NULL) { PyErr_SetString(LrErr_Exception, "Handle initialization failed"); return -1; } return 0; } static void handle_dealloc(_HandleObject *o) { if (o->handle) lr_handle_free(o->handle); Py_XDECREF(o->progress_cb); Py_XDECREF(o->progress_cb_data); Py_XDECREF(o->fastestmirror_cb); Py_XDECREF(o->fastestmirror_cb_data); Py_XDECREF(o->hmf_cb); Py_TYPE(o)->tp_free(o); } static PyObject * py_setopt(_HandleObject *self, PyObject *args) { int option; PyObject *obj; gboolean res = TRUE; GError *tmp_err = NULL; if (!PyArg_ParseTuple(args, "iO:py_setopt", &option, &obj)) return NULL; if (check_HandleStatus(self)) return NULL; switch (option) { /* * Options with string arguments (NULL is supported) */ case LRO_MIRRORLIST: case LRO_MIRRORLISTURL: case LRO_METALINKURL: case LRO_USERPWD: case LRO_PROXY: case LRO_PROXYUSERPWD: case LRO_DESTDIR: case LRO_USERAGENT: case LRO_FASTESTMIRRORCACHE: case LRO_GNUPGHOMEDIR: case LRO_SSLCLIENTCERT: case LRO_SSLCLIENTKEY: case LRO_SSLCACERT: case LRO_CACHEDIR: { char *str = NULL, *alloced = NULL; if (PyUnicode_Check(obj)) { PyObject *bytes = PyUnicode_AsUTF8String(obj); if (!bytes) return NULL; str = alloced = g_strdup(PyBytes_AsString(bytes)); Py_XDECREF(bytes); } else if (PyBytes_Check(obj)) { str = PyBytes_AsString(obj); } else if (obj != Py_None) { PyErr_SetString(PyExc_TypeError, "Only string or None is supported with this option"); return NULL; } res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, str); g_free(alloced); break; } /* * Options with double arguments */ case LRO_FASTESTMIRRORTIMEOUT: { double d; if (PyFloat_Check(obj)) d = PyFloat_AS_DOUBLE(obj); else if (obj == Py_None) { // None stands for default value d = LRO_FASTESTMIRRORTIMEOUT_DEFAULT; } else { PyErr_SetString(PyExc_TypeError, "Only float or None is supported with this option"); return NULL; } res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, d); break; } /* * Options with long/int (boolean) arguments */ case LRO_UPDATE: case LRO_LOCAL: case LRO_HTTPAUTH: case LRO_PROXYAUTH: case LRO_GPGCHECK: case LRO_IGNOREMISSING: case LRO_CHECKSUM: case LRO_INTERRUPTIBLE: case LRO_FETCHMIRRORS: case LRO_FASTESTMIRROR: case LRO_SSLVERIFYPEER: case LRO_SSLVERIFYHOST: case LRO_ADAPTIVEMIRRORSORTING: case LRO_FTPUSEEPSV: case LRO_PRESERVETIME: case LRO_OFFLINE: { long d; // Default values for None attribute if (obj == Py_None && (option == LRO_SSLVERIFYPEER || option == LRO_SSLVERIFYHOST)) { d = 1; } else if (obj == Py_None && option == LRO_ADAPTIVEMIRRORSORTING) { d = LRO_ADAPTIVEMIRRORSORTING_DEFAULT; } else if (obj == Py_None && option == LRO_FTPUSEEPSV) { d = LRO_FTPUSEEPSV_DEFAULT; // end of default attributes } else if (PyObject_IsTrue(obj) == 1) d = 1; else if (PyObject_IsTrue(obj) == 0) d = 0; else { PyErr_SetString(PyExc_TypeError, "Only Int, Long or Bool are supported with this option"); return NULL; } res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, d); break; } /* * Options with long/int arguments */ case LRO_PROXYTYPE: case LRO_REPOTYPE: case LRO_FASTESTMIRRORMAXAGE: case LRO_LOWSPEEDTIME: case LRO_LOWSPEEDLIMIT: case LRO_IPRESOLVE: case LRO_ALLOWEDMIRRORFAILURES: { int badarg = 0; long d; if (PyLong_Check(obj)) d = PyLong_AsLong(obj); #if PY_MAJOR_VERSION < 3 else if (PyInt_Check(obj)) d = PyInt_AS_LONG(obj); #endif else if (obj == Py_None) { // None stands for default value switch (option) { case LRO_PROXYTYPE: d = LRO_PROXYTYPE_DEFAULT; break; case LRO_LOWSPEEDTIME: d = LRO_LOWSPEEDTIME_DEFAULT; break; case LRO_LOWSPEEDLIMIT: d = LRO_LOWSPEEDLIMIT_DEFAULT; break; case LRO_FASTESTMIRRORMAXAGE: d = LRO_FASTESTMIRRORMAXAGE_DEFAULT; break; case LRO_IPRESOLVE: d = LRO_IPRESOLVE_DEFAULT; break; case LRO_ALLOWEDMIRRORFAILURES: d = LRO_ALLOWEDMIRRORFAILURES_DEFAULT; break; default: badarg = 1; } } else badarg = 1; if (badarg) { PyErr_SetString(PyExc_TypeError, "Only Int/Long is supported with this option"); return NULL; } res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, d); break; } /* * Options with long/int/None arguments */ case LRO_PROXYPORT: case LRO_CONNECTTIMEOUT: case LRO_MAXMIRRORTRIES: case LRO_MAXPARALLELDOWNLOADS: case LRO_MAXDOWNLOADSPERMIRROR: case LRO_HTTPAUTHMETHODS: case LRO_PROXYAUTHMETHODS: { long d; if (PyLong_Check(obj)) d = PyLong_AsLong(obj); #if PY_MAJOR_VERSION < 3 else if (PyInt_Check(obj)) d = PyInt_AS_LONG(obj); #endif else if (obj == Py_None) { /* Default options */ if (option == LRO_PROXYPORT) d = LRO_PROXYPORT_DEFAULT; else if (option == LRO_MAXMIRRORTRIES) d = LRO_MAXMIRRORTRIES_DEFAULT; else if (option == LRO_CONNECTTIMEOUT) d = LRO_CONNECTTIMEOUT_DEFAULT; else if (option == LRO_MAXPARALLELDOWNLOADS) d = LRO_MAXPARALLELDOWNLOADS_DEFAULT; else if (option == LRO_MAXDOWNLOADSPERMIRROR) d = LRO_MAXDOWNLOADSPERMIRROR_DEFAULT; else if (option == LRO_HTTPAUTHMETHODS) d = LRO_HTTPAUTHMETHODS_DEFAULT; else if (option == LRO_PROXYAUTHMETHODS) d = LRO_PROXYAUTHMETHODS_DEFAULT; else assert(0); } else { PyErr_SetString(PyExc_TypeError, "Only Int/Long/None is supported with this option"); return NULL; } res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, d); break; } /* * Options with gint64/None arguments */ case LRO_MAXSPEED: { gint64 d; if (PyLong_Check(obj)) d = (gint64) PyLong_AsLongLong(obj); #if PY_MAJOR_VERSION < 3 else if (PyInt_Check(obj)) d = (gint64) PyInt_AS_LONG(obj); #endif else if (obj == Py_None) { /* Default options */ if (option == LRO_MAXSPEED) d = (gint64) LRO_MAXSPEED_DEFAULT; else assert(0); } else { PyErr_SetString(PyExc_TypeError, "Only Int/Long/None is supported with this option"); return NULL; } res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, d); break; } /* * Options with array argument */ case LRO_URLS: case LRO_YUMDLIST: case LRO_YUMBLIST: case LRO_HTTPHEADER: { Py_ssize_t len = 0; if (!PyList_Check(obj) && obj != Py_None) { PyErr_SetString(PyExc_TypeError, "Only List or None type is supported with this option"); return NULL; } if (obj == Py_None) { res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, NULL); break; } len = PyList_Size(obj); for (Py_ssize_t x = 0; x < len; x++) { PyObject *item = PyList_GetItem(obj, x); if (!PyBytes_Check(item) && !PyUnicode_Check(item) && item != Py_None) { PyErr_SetString(PyExc_TypeError, "Only strings or Nones are supported in list"); return NULL; } } GStringChunk *chunk = g_string_chunk_new(0); GPtrArray *ptrarray = g_ptr_array_sized_new(len + 1); for (Py_ssize_t x = 0; x < len; x++) { PyObject *item = PyList_GetItem(obj, x); if (PyUnicode_Check(item)) { PyObject *bytes = PyUnicode_AsUTF8String(item); if (!bytes) { g_ptr_array_free(ptrarray, TRUE); g_string_chunk_free(chunk); return NULL; } char *item_str = g_string_chunk_insert(chunk, PyBytes_AsString(bytes)); Py_XDECREF(bytes); g_ptr_array_add(ptrarray, item_str); } if (PyBytes_Check(item)) g_ptr_array_add(ptrarray, PyBytes_AsString(item)); } g_ptr_array_add(ptrarray, NULL); res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, ptrarray->pdata); g_string_chunk_free(chunk); g_ptr_array_free(ptrarray, TRUE); break; } case LRO_YUMSLIST: case LRO_VARSUB: { Py_ssize_t len = 0; LrUrlVars *vars = NULL; if (!PyList_Check(obj) && obj != Py_None) { PyErr_SetString(PyExc_TypeError, "Only List of tuples or None type is supported with this option"); return NULL; } if (obj == Py_None) { res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, NULL); break; } // Check all list elements len = PyList_Size(obj); for (Py_ssize_t x = 0; x < len; x++) { PyObject *item = PyList_GetItem(obj, x); PyObject *tuple_item; if (!PyTuple_Check(item) || PyTuple_Size(item) != 2) { PyErr_SetString(PyExc_TypeError, "List elements has to be " "tuples with exactly 2 elements"); return NULL; } tuple_item = PyTuple_GetItem(item, 1); if ((!PyBytes_Check(PyTuple_GetItem(item, 0)) && !PyUnicode_Check(PyTuple_GetItem(item, 0))) || (!PyBytes_Check(tuple_item) && !PyUnicode_Check(tuple_item) && tuple_item != Py_None)) { PyErr_SetString(PyExc_TypeError, "Bad list format"); return NULL; } } GStringChunk *chunk = g_string_chunk_new(0); for (Py_ssize_t x = 0; x < len; x++) { PyObject *item = PyList_GetItem(obj, x); PyObject *tuple_item; char *var, *val; tuple_item = PyTuple_GetItem(item, 0); if (PyBytes_Check(tuple_item)) { // PyBytes var = PyBytes_AsString(tuple_item); } else { // PyUnicode PyObject *bytes = PyUnicode_AsUTF8String(tuple_item); if (!bytes) { lr_urlvars_free(vars); g_string_chunk_free(chunk); return NULL; } char *item_str = g_string_chunk_insert(chunk, PyBytes_AsString(bytes)); Py_XDECREF(bytes); var = item_str; } tuple_item = PyTuple_GetItem(item, 1); if (tuple_item == Py_None) { // Py_None val = NULL; } else if (PyBytes_Check(tuple_item)) { // PyBytes val = PyBytes_AsString(tuple_item); } else { // PyUnicode PyObject *bytes = PyUnicode_AsUTF8String(tuple_item); if (!bytes) { lr_urlvars_free(vars); g_string_chunk_free(chunk); return NULL; } char *item_str = g_string_chunk_insert(chunk, PyBytes_AsString(bytes)); Py_XDECREF(bytes); val = item_str; } vars = lr_urlvars_set(vars, var, val); } res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, vars); g_string_chunk_free(chunk); break; } /* * Options with callable arguments */ case LRO_PROGRESSCB: { if (!PyCallable_Check(obj) && obj != Py_None) { PyErr_SetString(PyExc_TypeError, "Only callable argument or None is supported with this option"); return NULL; } Py_XDECREF(self->progress_cb); if (obj == Py_None) { // None object self->progress_cb = NULL; res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, NULL); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); } else { // New callback object Py_XINCREF(obj); self->progress_cb = obj; res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, progress_callback); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); res = lr_handle_setopt(self->handle, &tmp_err, LRO_PROGRESSDATA, self); } break; } case LRO_FASTESTMIRRORCB: { if (!PyCallable_Check(obj) && obj != Py_None) { PyErr_SetString(PyExc_TypeError, "Only callable argument or None is supported with this option"); return NULL; } Py_XDECREF(self->fastestmirror_cb); if (obj == Py_None) { // None object self->fastestmirror_cb = NULL; res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, NULL); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); } else { // New callback object Py_XINCREF(obj); self->fastestmirror_cb = obj; res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, fastestmirror_callback); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); res = lr_handle_setopt(self->handle, &tmp_err, LRO_FASTESTMIRRORDATA, self); } break; } case LRO_HMFCB: { if (!PyCallable_Check(obj) && obj != Py_None) { PyErr_SetString(PyExc_TypeError, "Only callable argument or None is supported with this option"); return NULL; } Py_XDECREF(self->hmf_cb); if (obj == Py_None) { // None object self->hmf_cb = NULL; res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, NULL); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); } else { // New callback object Py_XINCREF(obj); self->hmf_cb = obj; res = lr_handle_setopt(self->handle, &tmp_err, (LrHandleOption)option, hmf_callback); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); res = lr_handle_setopt(self->handle, &tmp_err, LRO_PROGRESSDATA, self); } break; } /* * Options with callback data */ case LRO_PROGRESSDATA: { if (obj == Py_None) { self->progress_cb_data = NULL; } else { Py_XINCREF(obj); self->progress_cb_data = obj; } break; } case LRO_FASTESTMIRRORDATA: { if (obj == Py_None) { self->fastestmirror_cb_data = NULL; } else { Py_XINCREF(obj); self->fastestmirror_cb_data = obj; } break; } /* * Unknown options */ default: PyErr_SetString(PyExc_ValueError, "Unknown option"); return NULL; } if (!res) RETURN_ERROR(&tmp_err, -1, NULL); Py_RETURN_NONE; } static PyObject * py_getinfo(_HandleObject *self, PyObject *args) { int option; gboolean res = TRUE; char *str; long lval; double dval; GError *tmp_err = NULL; if (!PyArg_ParseTuple(args, "i:py_getinfo", &option)) return NULL; if (check_HandleStatus(self)) return NULL; switch (option) { /* char** options */ case LRI_MIRRORLIST: case LRI_MIRRORLISTURL: case LRI_METALINKURL: case LRI_DESTDIR: case LRI_USERAGENT: case LRI_FASTESTMIRRORCACHE: case LRI_GNUPGHOMEDIR: case LRI_SSLCLIENTCERT: case LRI_SSLCLIENTKEY: case LRI_SSLCACERT: case LRI_CACHEDIR: res = lr_handle_getinfo(self->handle, &tmp_err, (LrHandleInfoOption)option, &str); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); return PyStringOrNone_FromString(str); /* double* options */ case LRI_FASTESTMIRRORTIMEOUT: res = lr_handle_getinfo(self->handle, &tmp_err, (LrHandleInfoOption)option, &dval); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); return PyFloat_FromDouble(dval); /* long* options */ case LRI_UPDATE: case LRI_LOCAL: case LRI_REPOTYPE: case LRI_FETCHMIRRORS: case LRI_MAXMIRRORTRIES: case LRI_FASTESTMIRROR: case LRI_FASTESTMIRRORMAXAGE: case LRI_SSLVERIFYPEER: case LRI_SSLVERIFYHOST: case LRI_ALLOWEDMIRRORFAILURES: case LRI_ADAPTIVEMIRRORSORTING: case LRI_OFFLINE: case LRI_LOWSPEEDTIME: case LRI_LOWSPEEDLIMIT: case LRI_FTPUSEEPSV: res = lr_handle_getinfo(self->handle, &tmp_err, (LrHandleInfoOption)option, &lval); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); return PyLong_FromLong(lval); /* LrAuth* option */ case LRI_HTTPAUTHMETHODS: case LRI_PROXYAUTHMETHODS: { LrAuth auth = 0; res = lr_handle_getinfo(self->handle, &tmp_err, (LrHandleInfoOption)option, &auth); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); return PyLong_FromLong((long) auth); } /* LrIpResolveType* option */ case LRI_IPRESOLVE: { LrIpResolveType type; res = lr_handle_getinfo(self->handle, &tmp_err, (LrHandleInfoOption)option, &type); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); return PyLong_FromLong((long) type); } /* List option */ case LRI_YUMSLIST: case LRI_VARSUB: { LrUrlVars *vars; PyObject *list; res = lr_handle_getinfo(self->handle, &tmp_err, (LrHandleInfoOption)option, &vars); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); if (vars == NULL) Py_RETURN_NONE; list = PyList_New(0); for (LrUrlVars *elem = vars; elem; elem = g_slist_next(elem)) { PyObject *tuple, *obj; LrVar *var = elem->data; tuple = PyTuple_New(2); obj = PyStringOrNone_FromString(var->var); PyTuple_SetItem(tuple, 0, obj); if (var->val != NULL) { obj = PyStringOrNone_FromString(var->val); } else { Py_INCREF(Py_None); obj = Py_None; } PyTuple_SetItem(tuple, 1, obj); PyList_Append(list, tuple); } return list; } /* char*** options */ case LRI_URLS: case LRI_YUMDLIST: case LRI_YUMBLIST: case LRI_MIRRORS: case LRI_HTTPHEADER: { PyObject *list; char **strlist; res = lr_handle_getinfo(self->handle, &tmp_err, (LrHandleInfoOption)option, &strlist); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); if (strlist == NULL) { if (option == LRI_MIRRORS || option == LRI_URLS) { return PyList_New(0); } else { Py_RETURN_NONE; } } list = PyList_New(0); for (int x=0; strlist[x] != NULL; x++) { PyList_Append(list, PyStringOrNone_FromString(strlist[x])); } g_strfreev(strlist); return list; } /* callback option */ case LRI_PROGRESSCB: if (self->progress_cb == NULL) Py_RETURN_NONE; Py_INCREF(self->progress_cb); return self->progress_cb; /* callback data options */ case LRI_PROGRESSDATA: if (self->progress_cb_data == NULL) Py_RETURN_NONE; Py_INCREF(self->progress_cb_data); return self->progress_cb_data; case LRI_HMFCB: if (self->hmf_cb == NULL) Py_RETURN_NONE; Py_INCREF(self->hmf_cb); return self->hmf_cb; /* metalink */ case LRI_METALINK: { PyObject *py_metalink; LrMetalink *metalink; res = lr_handle_getinfo(self->handle, &tmp_err, (LrHandleInfoOption)option, &metalink); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); if (metalink == NULL) Py_RETURN_NONE; py_metalink = PyObject_FromMetalink(metalink); return py_metalink; } default: PyErr_SetString(PyExc_ValueError, "Unknown option"); return NULL; } assert(res); Py_RETURN_NONE; } static PyObject * py_perform(_HandleObject *self, PyObject *args) { PyObject *result_obj; LrResult *result; gboolean ret; GError *tmp_err = NULL; PyThreadState *state = NULL; if (!PyArg_ParseTuple(args, "O:py_perform", &result_obj)) return NULL; if (check_HandleStatus(self)) return NULL; result = Result_FromPyObject(result_obj); Handle_SetThreadState((PyObject *) self, &state); // XXX: GIL Hack int hack_rc = gil_logger_hack_begin(&state); if (hack_rc == GIL_HACK_ERROR) return NULL; BeginAllowThreads(&state); ret = lr_handle_perform(self->handle, result, &tmp_err); EndAllowThreads(&state); // XXX: GIL Hack if (!gil_logger_hack_end(hack_rc)) return NULL; assert((ret && !tmp_err) || (!ret && tmp_err)); if (ret) Py_RETURN_NONE; // All fine - Return None // Error occurred if (PyErr_Occurred()) { // Python exception occurred (in a python callback probably) return NULL; } else if(tmp_err->code == LRE_INTERRUPTED) { // Interrupted by Ctr+C g_error_free(tmp_err); PyErr_SetInterrupt(); PyErr_CheckSignals(); return NULL; } else { // Return exception created from GError RETURN_ERROR(&tmp_err, -1, NULL); } } static PyObject * py_download_package(_HandleObject *self, PyObject *args) { gboolean ret; char *relative_url, *checksum, *dest, *base_url; int resume, checksum_type; PY_LONG_LONG expectedsize; GError *tmp_err = NULL; PyThreadState *state = NULL; if (!PyArg_ParseTuple(args, "szizLzi:py_download_package", &relative_url, &dest, &checksum_type, &checksum, &expectedsize, &base_url, &resume)) return NULL; if (check_HandleStatus(self)) return NULL; Handle_SetThreadState((PyObject *) self, &state); // XXX: GIL Hack int hack_rc = gil_logger_hack_begin(&state); if (hack_rc == GIL_HACK_ERROR) return NULL; BeginAllowThreads(&state); ret = lr_download_package(self->handle, relative_url, dest, checksum_type, checksum, (gint64) expectedsize, base_url, resume, &tmp_err); EndAllowThreads(&state); // XXX: GIL Hack if (!gil_logger_hack_end(hack_rc)) return NULL; assert((ret && !tmp_err) || (!ret && tmp_err)); if (!ret && tmp_err->code == LRE_INTERRUPTED) { g_error_free(tmp_err); PyErr_SetInterrupt(); PyErr_CheckSignals(); return NULL; } if (ret) Py_RETURN_NONE; // All fine - Return None // Error occurred if (PyErr_Occurred()) { // Python exception occurred (in a python callback probably) return NULL; } else if(tmp_err->code == LRE_INTERRUPTED) { // Interrupted by Ctr+C g_error_free(tmp_err); PyErr_SetInterrupt(); PyErr_CheckSignals(); return NULL; } else { // Return exception created from GError RETURN_ERROR(&tmp_err, -1, NULL); } } static struct PyMethodDef handle_methods[] = { { "setopt", (PyCFunction)py_setopt, METH_VARARGS, NULL }, { "getinfo", (PyCFunction)py_getinfo, METH_VARARGS, NULL }, { "perform", (PyCFunction)py_perform, METH_VARARGS, NULL }, { "download_package", (PyCFunction)py_download_package, METH_VARARGS, NULL }, { NULL } }; PyTypeObject Handle_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_librepo.Handle", /* tp_name */ sizeof(_HandleObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor) handle_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ "Handle object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ 0, /* tp_iternext */ handle_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc) handle_init, /* tp_init */ 0, /* tp_alloc */ handle_new, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ }; librepo-1.12.1/librepo/python/handle-py.h000066400000000000000000000023231372027637700202550ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_HANDLE_PY_H__ #define __LR_HANDLE_PY_H__ #include "librepo/librepo.h" extern PyTypeObject Handle_Type; #define HandleObject_Check(o) PyObject_TypeCheck(o, &Handle_Type) LrHandle *Handle_FromPyObject(PyObject *o); void Handle_SetThreadState(PyObject *o, PyThreadState **state); #endif librepo-1.12.1/librepo/python/librepomodule.c000066400000000000000000000372771372027637700212510ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "librepo/librepo.h" #include "exception-py.h" #include "handle-py.h" #include "metadatadownloader-py.h" #include "metadatatarget-py.h" #include "packagedownloader-py.h" #include "packagetarget-py.h" #include "result-py.h" #include "yum-py.h" #include "downloader-py.h" #include "globalstate-py.h" // GIL Hack #include "typeconversion.h" #include "logging.h" volatile int global_logger = 0; volatile PyThreadState **global_state = NULL; PyObject *debug_cb = NULL; PyObject *debug_cb_data = NULL; gint debug_handler_id = -1; G_LOCK_DEFINE(gil_hack_lock); void py_debug_cb(G_GNUC_UNUSED const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level, const gchar *message, G_GNUC_UNUSED gpointer user_data) { PyObject *arglist, *data, *result, *py_message; if (!debug_cb) return; // XXX: GIL Hack if (global_state) EndAllowThreads((PyThreadState **) global_state); // XXX: End of GIL Hack py_message = PyStringOrNone_FromString(message); data = (debug_cb_data) ? debug_cb_data : Py_None; arglist = Py_BuildValue("(OO)", py_message, data); result = PyObject_CallObject(debug_cb, arglist); Py_DECREF(arglist); Py_XDECREF(result); Py_DECREF(py_message); // XXX: GIL Hack if (global_state) BeginAllowThreads((PyThreadState **) global_state); // XXX: End of GIL Hack } PyObject * py_set_debug_log_handler(G_GNUC_UNUSED PyObject *self, PyObject *args) { PyObject *cb, *cb_data = NULL; if (!PyArg_ParseTuple(args, "O|O:py_set_debug_log_handler", &cb, &cb_data)) return NULL; if (cb == Py_None) cb = NULL; if (cb && !PyCallable_Check(cb)) { PyErr_SetString(PyExc_TypeError, "parameter must be callable"); return NULL; } Py_XDECREF(debug_cb); Py_XDECREF(debug_cb_data); debug_cb = cb; debug_cb_data = cb_data; Py_XINCREF(debug_cb); Py_XINCREF(debug_cb_data); if (debug_cb) { debug_handler_id = g_log_set_handler("librepo", G_LOG_LEVEL_DEBUG, py_debug_cb, NULL); global_logger = 1; } else if (debug_handler_id != -1) { g_log_remove_handler("librepo", debug_handler_id); } Py_RETURN_NONE; } static struct PyMethodDef librepo_methods[] = { { "yum_repomd_get_age", (PyCFunction)py_yum_repomd_get_age, METH_VARARGS, NULL }, { "set_debug_log_handler", (PyCFunction)py_set_debug_log_handler, METH_VARARGS, NULL }, { "download_packages", (PyCFunction)py_download_packages, METH_VARARGS, NULL }, { "download_metadata", (PyCFunction)py_download_metadata, METH_VARARGS, NULL }, { "download_url", (PyCFunction)py_download_url, METH_VARARGS, NULL }, { "log_set_file", (PyCFunction)py_log_set_file, METH_VARARGS, NULL }, { "log_remove_handler", (PyCFunction)py_log_remove_handler, METH_VARARGS, NULL }, { NULL } }; void exit_librepo(void) { Py_XDECREF(debug_cb); Py_XDECREF(debug_cb_data); } struct module_state { PyObject *error; }; #if PY_MAJOR_VERSION >= 3 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) #else #define GETSTATE(m) (&_state) static struct module_state _state; #endif #if PY_MAJOR_VERSION >= 3 static int librepo_traverse(PyObject *m, visitproc visit, void *arg) { Py_VISIT(GETSTATE(m)->error); return 0; } static int librepo_clear(PyObject *m) { remove_all_log_handlers(); Py_CLEAR(GETSTATE(m)->error); return 0; } static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_librepo", "A library providing C and Python (libcURL like) API for downloading " "linux repository metadata and packages", sizeof(struct module_state), librepo_methods, NULL, librepo_traverse, librepo_clear, NULL }; #define INITERROR return NULL PyObject * PyInit__librepo(void) #else #define INITERROR return void init_librepo(void) #endif { #if PY_MAJOR_VERSION >= 3 PyObject *m = PyModule_Create(&moduledef); #else PyObject *m = Py_InitModule("_librepo", librepo_methods); #endif if (!m) INITERROR; struct module_state *st = GETSTATE(m); // Exceptions if (!init_exceptions()) { Py_DECREF(m); INITERROR; } st->error = LrErr_Exception; PyModule_AddObject(m, "LibrepoException", LrErr_Exception); // Objects // _librepo.Handle if (PyType_Ready(&Handle_Type) < 0) INITERROR; Py_INCREF(&Handle_Type); PyModule_AddObject(m, "Handle", (PyObject *)&Handle_Type); // _librepo.Result if (PyType_Ready(&Result_Type) < 0) INITERROR; Py_INCREF(&Result_Type); PyModule_AddObject(m, "Result", (PyObject *)&Result_Type); // _librepo.PackageTarget if (PyType_Ready(&PackageTarget_Type) < 0) INITERROR; Py_INCREF(&PackageTarget_Type); PyModule_AddObject(m, "PackageTarget", (PyObject *)&PackageTarget_Type); // _librepo.MetadataTarget if (PyType_Ready(&MetadataTarget_Type) < 0) INITERROR; Py_INCREF(&MetadataTarget_Type); PyModule_AddObject(m, "MetadataTarget", (PyObject *)&MetadataTarget_Type); // Init module Py_AtExit(exit_librepo); // Module constants #define PYMODULE_ADDINTCONSTANT(name) PyModule_AddIntConstant(m, #name, (name)) #define PYMODULE_ADDSTRCONSTANT(name) PyModule_AddStringConstant(m, #name, (name)) // Version PYMODULE_ADDINTCONSTANT(LR_VERSION_MAJOR); PYMODULE_ADDINTCONSTANT(LR_VERSION_MINOR); PYMODULE_ADDINTCONSTANT(LR_VERSION_PATCH); PYMODULE_ADDSTRCONSTANT(LR_VERSION); // Handle options PYMODULE_ADDINTCONSTANT(LRO_UPDATE); PYMODULE_ADDINTCONSTANT(LRO_URLS); PYMODULE_ADDINTCONSTANT(LRO_MIRRORLIST); PYMODULE_ADDINTCONSTANT(LRO_MIRRORLISTURL); PYMODULE_ADDINTCONSTANT(LRO_METALINKURL); PYMODULE_ADDINTCONSTANT(LRO_LOCAL); PYMODULE_ADDINTCONSTANT(LRO_HTTPAUTH); PYMODULE_ADDINTCONSTANT(LRO_USERPWD); PYMODULE_ADDINTCONSTANT(LRO_PROXY); PYMODULE_ADDINTCONSTANT(LRO_PROXYPORT); PYMODULE_ADDINTCONSTANT(LRO_PROXYTYPE); PYMODULE_ADDINTCONSTANT(LRO_PROXYAUTH); PYMODULE_ADDINTCONSTANT(LRO_PROXYUSERPWD); PYMODULE_ADDINTCONSTANT(LRO_PROGRESSCB); PYMODULE_ADDINTCONSTANT(LRO_PROGRESSDATA); PYMODULE_ADDINTCONSTANT(LRO_MAXSPEED); PYMODULE_ADDINTCONSTANT(LRO_DESTDIR); PYMODULE_ADDINTCONSTANT(LRO_REPOTYPE); PYMODULE_ADDINTCONSTANT(LRO_CONNECTTIMEOUT); PYMODULE_ADDINTCONSTANT(LRO_IGNOREMISSING); PYMODULE_ADDINTCONSTANT(LRO_INTERRUPTIBLE); PYMODULE_ADDINTCONSTANT(LRO_USERAGENT); PYMODULE_ADDINTCONSTANT(LRO_FETCHMIRRORS); PYMODULE_ADDINTCONSTANT(LRO_MAXMIRRORTRIES); PYMODULE_ADDINTCONSTANT(LRO_MAXPARALLELDOWNLOADS); PYMODULE_ADDINTCONSTANT(LRO_MAXDOWNLOADSPERMIRROR); PYMODULE_ADDINTCONSTANT(LRO_VARSUB); PYMODULE_ADDINTCONSTANT(LRO_FASTESTMIRROR); PYMODULE_ADDINTCONSTANT(LRO_FASTESTMIRRORCACHE); PYMODULE_ADDINTCONSTANT(LRO_FASTESTMIRRORMAXAGE); PYMODULE_ADDINTCONSTANT(LRO_FASTESTMIRRORCB); PYMODULE_ADDINTCONSTANT(LRO_FASTESTMIRRORDATA); PYMODULE_ADDINTCONSTANT(LRO_LOWSPEEDTIME); PYMODULE_ADDINTCONSTANT(LRO_LOWSPEEDLIMIT); PYMODULE_ADDINTCONSTANT(LRO_GPGCHECK); PYMODULE_ADDINTCONSTANT(LRO_CHECKSUM); PYMODULE_ADDINTCONSTANT(LRO_YUMDLIST); PYMODULE_ADDINTCONSTANT(LRO_YUMSLIST); PYMODULE_ADDINTCONSTANT(LRO_RPMMDDLIST); PYMODULE_ADDINTCONSTANT(LRO_YUMBLIST); PYMODULE_ADDINTCONSTANT(LRO_RPMMDBLIST); PYMODULE_ADDINTCONSTANT(LRO_HMFCB); PYMODULE_ADDINTCONSTANT(LRO_SSLVERIFYPEER); PYMODULE_ADDINTCONSTANT(LRO_SSLVERIFYHOST); PYMODULE_ADDINTCONSTANT(LRO_SSLCLIENTCERT); PYMODULE_ADDINTCONSTANT(LRO_SSLCLIENTKEY); PYMODULE_ADDINTCONSTANT(LRO_SSLCACERT); PYMODULE_ADDINTCONSTANT(LRO_IPRESOLVE); PYMODULE_ADDINTCONSTANT(LRO_ALLOWEDMIRRORFAILURES); PYMODULE_ADDINTCONSTANT(LRO_ADAPTIVEMIRRORSORTING); PYMODULE_ADDINTCONSTANT(LRO_GNUPGHOMEDIR); PYMODULE_ADDINTCONSTANT(LRO_FASTESTMIRRORTIMEOUT); PYMODULE_ADDINTCONSTANT(LRO_HTTPHEADER); PYMODULE_ADDINTCONSTANT(LRO_OFFLINE); PYMODULE_ADDINTCONSTANT(LRO_HTTPAUTHMETHODS); PYMODULE_ADDINTCONSTANT(LRO_PROXYAUTHMETHODS); PYMODULE_ADDINTCONSTANT(LRO_FTPUSEEPSV); PYMODULE_ADDINTCONSTANT(LRO_CACHEDIR); PYMODULE_ADDINTCONSTANT(LRO_PRESERVETIME); PYMODULE_ADDINTCONSTANT(LRO_SENTINEL); // Handle info options PYMODULE_ADDINTCONSTANT(LRI_UPDATE); PYMODULE_ADDINTCONSTANT(LRI_URLS); PYMODULE_ADDINTCONSTANT(LRI_MIRRORLIST); PYMODULE_ADDINTCONSTANT(LRI_MIRRORLISTURL); PYMODULE_ADDINTCONSTANT(LRI_METALINKURL); PYMODULE_ADDINTCONSTANT(LRI_LOCAL); PYMODULE_ADDINTCONSTANT(LRI_PROGRESSCB); PYMODULE_ADDINTCONSTANT(LRI_PROGRESSDATA); PYMODULE_ADDINTCONSTANT(LRI_DESTDIR); PYMODULE_ADDINTCONSTANT(LRI_REPOTYPE); PYMODULE_ADDINTCONSTANT(LRI_USERAGENT); PYMODULE_ADDINTCONSTANT(LRI_YUMDLIST); PYMODULE_ADDINTCONSTANT(LRI_YUMSLIST); PYMODULE_ADDINTCONSTANT(LRI_RPMMDDLIST); PYMODULE_ADDINTCONSTANT(LRI_YUMBLIST); PYMODULE_ADDINTCONSTANT(LRI_RPMMDBLIST); PYMODULE_ADDINTCONSTANT(LRI_FETCHMIRRORS); PYMODULE_ADDINTCONSTANT(LRI_MAXMIRRORTRIES); PYMODULE_ADDINTCONSTANT(LRI_VARSUB); PYMODULE_ADDINTCONSTANT(LRI_MIRRORS); PYMODULE_ADDINTCONSTANT(LRI_METALINK); PYMODULE_ADDINTCONSTANT(LRI_FASTESTMIRROR); PYMODULE_ADDINTCONSTANT(LRI_FASTESTMIRRORCACHE); PYMODULE_ADDINTCONSTANT(LRI_FASTESTMIRRORMAXAGE); PYMODULE_ADDINTCONSTANT(LRI_HMFCB); PYMODULE_ADDINTCONSTANT(LRI_SSLVERIFYPEER); PYMODULE_ADDINTCONSTANT(LRI_SSLVERIFYHOST); PYMODULE_ADDINTCONSTANT(LRI_SSLCLIENTCERT); PYMODULE_ADDINTCONSTANT(LRI_SSLCLIENTKEY); PYMODULE_ADDINTCONSTANT(LRI_SSLCACERT); PYMODULE_ADDINTCONSTANT(LRI_IPRESOLVE); PYMODULE_ADDINTCONSTANT(LRI_ALLOWEDMIRRORFAILURES); PYMODULE_ADDINTCONSTANT(LRI_ADAPTIVEMIRRORSORTING); PYMODULE_ADDINTCONSTANT(LRI_GNUPGHOMEDIR); PYMODULE_ADDINTCONSTANT(LRI_FASTESTMIRRORTIMEOUT); PYMODULE_ADDINTCONSTANT(LRI_HTTPHEADER); PYMODULE_ADDINTCONSTANT(LRI_OFFLINE); PYMODULE_ADDINTCONSTANT(LRI_LOWSPEEDTIME); PYMODULE_ADDINTCONSTANT(LRI_LOWSPEEDLIMIT); PYMODULE_ADDINTCONSTANT(LRI_HTTPAUTHMETHODS); PYMODULE_ADDINTCONSTANT(LRI_PROXYAUTHMETHODS); PYMODULE_ADDINTCONSTANT(LRI_FTPUSEEPSV); PYMODULE_ADDINTCONSTANT(LRI_CACHEDIR); PYMODULE_ADDINTCONSTANT(LRI_SENTINEL); // Check options PYMODULE_ADDINTCONSTANT(LR_CHECK_GPG); PYMODULE_ADDINTCONSTANT(LR_CHECK_CHECKSUM); // Repo type PYMODULE_ADDINTCONSTANT(LR_YUMREPO); PYMODULE_ADDINTCONSTANT(LR_RPMMDREPO); PYMODULE_ADDINTCONSTANT(LR_SUSEREPO); PYMODULE_ADDINTCONSTANT(LR_DEBREPO); // Proxy type PYMODULE_ADDINTCONSTANT(LR_PROXY_HTTP); PYMODULE_ADDINTCONSTANT(LR_PROXY_HTTP_1_0); PYMODULE_ADDINTCONSTANT(LR_PROXY_SOCKS4); PYMODULE_ADDINTCONSTANT(LR_PROXY_SOCKS5); PYMODULE_ADDINTCONSTANT(LR_PROXY_SOCKS4A); PYMODULE_ADDINTCONSTANT(LR_PROXY_SOCKS5_HOSTNAME); // IpResolve type PYMODULE_ADDINTCONSTANT(LR_IPRESOLVE_WHATEVER); PYMODULE_ADDINTCONSTANT(LR_IPRESOLVE_V4); PYMODULE_ADDINTCONSTANT(LR_IPRESOLVE_V6); // Return codes PYMODULE_ADDINTCONSTANT(LRE_OK); PYMODULE_ADDINTCONSTANT(LRE_BADFUNCARG); PYMODULE_ADDINTCONSTANT(LRE_BADOPTARG); PYMODULE_ADDINTCONSTANT(LRE_UNKNOWNOPT); PYMODULE_ADDINTCONSTANT(LRE_CURLSETOPT); PYMODULE_ADDINTCONSTANT(LRE_ALREADYUSEDRESULT); PYMODULE_ADDINTCONSTANT(LRE_INCOMPLETERESULT); PYMODULE_ADDINTCONSTANT(LRE_CURLDUP); PYMODULE_ADDINTCONSTANT(LRE_CURL); PYMODULE_ADDINTCONSTANT(LRE_CURLM); PYMODULE_ADDINTCONSTANT(LRE_BADSTATUS); PYMODULE_ADDINTCONSTANT(LRE_TEMPORARYERR); PYMODULE_ADDINTCONSTANT(LRE_NOTLOCAL); PYMODULE_ADDINTCONSTANT(LRE_CANNOTCREATEDIR); PYMODULE_ADDINTCONSTANT(LRE_IO); PYMODULE_ADDINTCONSTANT(LRE_MLBAD); PYMODULE_ADDINTCONSTANT(LRE_MLXML); PYMODULE_ADDINTCONSTANT(LRE_BADCHECKSUM); PYMODULE_ADDINTCONSTANT(LRE_REPOMDXML); PYMODULE_ADDINTCONSTANT(LRE_NOURL); PYMODULE_ADDINTCONSTANT(LRE_CANNOTCREATETMP); PYMODULE_ADDINTCONSTANT(LRE_UNKNOWNCHECKSUM); PYMODULE_ADDINTCONSTANT(LRE_BADURL); PYMODULE_ADDINTCONSTANT(LRE_GPGNOTSUPPORTED); PYMODULE_ADDINTCONSTANT(LRE_GPGERROR); PYMODULE_ADDINTCONSTANT(LRE_BADGPG); PYMODULE_ADDINTCONSTANT(LRE_INCOMPLETEREPO); PYMODULE_ADDINTCONSTANT(LRE_INTERRUPTED); PYMODULE_ADDINTCONSTANT(LRE_SIGACTION); PYMODULE_ADDINTCONSTANT(LRE_ALREADYDOWNLOADED); PYMODULE_ADDINTCONSTANT(LRE_UNFINISHED); PYMODULE_ADDINTCONSTANT(LRE_SELECT); PYMODULE_ADDINTCONSTANT(LRE_OPENSSL); PYMODULE_ADDINTCONSTANT(LRE_MEMORY); PYMODULE_ADDINTCONSTANT(LRE_XMLPARSER); PYMODULE_ADDINTCONSTANT(LRE_CBINTERRUPTED); PYMODULE_ADDINTCONSTANT(LRE_REPOMD); PYMODULE_ADDINTCONSTANT(LRE_VALUE); PYMODULE_ADDINTCONSTANT(LRE_NOTSET); PYMODULE_ADDINTCONSTANT(LRE_FILE); PYMODULE_ADDINTCONSTANT(LRE_KEYFILE); PYMODULE_ADDINTCONSTANT(LRE_UNKNOWNERROR); // Result option PYMODULE_ADDINTCONSTANT(LRR_YUM_REPO); PYMODULE_ADDINTCONSTANT(LRR_RPMMD_REPOMD); PYMODULE_ADDINTCONSTANT(LRR_YUM_TIMESTAMP); PYMODULE_ADDINTCONSTANT(LRR_RPMMD_REPO); PYMODULE_ADDINTCONSTANT(LRR_YUM_REPOMD); PYMODULE_ADDINTCONSTANT(LRR_RPMMD_TIMESTAMP); PYMODULE_ADDINTCONSTANT(LRR_SENTINEL); // Checksums PYMODULE_ADDINTCONSTANT(LR_CHECKSUM_UNKNOWN); PYMODULE_ADDINTCONSTANT(LR_CHECKSUM_MD5); PYMODULE_ADDINTCONSTANT(LR_CHECKSUM_SHA1); PYMODULE_ADDINTCONSTANT(LR_CHECKSUM_SHA224); PYMODULE_ADDINTCONSTANT(LR_CHECKSUM_SHA256); PYMODULE_ADDINTCONSTANT(LR_CHECKSUM_SHA384); PYMODULE_ADDINTCONSTANT(LR_CHECKSUM_SHA512); // Transfer statuses PYMODULE_ADDINTCONSTANT(LR_TRANSFER_SUCCESSFUL); PYMODULE_ADDINTCONSTANT(LR_TRANSFER_ALREADYEXISTS); PYMODULE_ADDINTCONSTANT(LR_TRANSFER_ERROR); // Fastest mirror stages PYMODULE_ADDINTCONSTANT(LR_FMSTAGE_INIT); PYMODULE_ADDINTCONSTANT(LR_FMSTAGE_CACHELOADING); PYMODULE_ADDINTCONSTANT(LR_FMSTAGE_CACHELOADINGSTATUS); PYMODULE_ADDINTCONSTANT(LR_FMSTAGE_DETECTION); PYMODULE_ADDINTCONSTANT(LR_FMSTAGE_FINISHING); PYMODULE_ADDINTCONSTANT(LR_FMSTAGE_STATUS); // Callbacks return values PYMODULE_ADDINTCONSTANT(LR_CB_OK); PYMODULE_ADDINTCONSTANT(LR_CB_ABORT); PYMODULE_ADDINTCONSTANT(LR_CB_ERROR); // Auth methods PYMODULE_ADDINTCONSTANT(LR_AUTH_NONE); PYMODULE_ADDINTCONSTANT(LR_AUTH_BASIC); PYMODULE_ADDINTCONSTANT(LR_AUTH_DIGEST); PYMODULE_ADDINTCONSTANT(LR_AUTH_NEGOTIATE); PYMODULE_ADDINTCONSTANT(LR_AUTH_NTLM); PYMODULE_ADDINTCONSTANT(LR_AUTH_DIGEST_IE); PYMODULE_ADDINTCONSTANT(LR_AUTH_NTLM_WB); PYMODULE_ADDINTCONSTANT(LR_AUTH_ONLY); PYMODULE_ADDINTCONSTANT(LR_AUTH_ANY); // Init librepo library lr_global_init(); #if PY_MAJOR_VERSION >= 3 return m; #endif } librepo-1.12.1/librepo/python/logging.c000066400000000000000000000107651372027637700200260ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2015 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #undef NDEBUG #include #include "librepo/librepo.h" #include "librepo/cleanup.h" #include "typeconversion.h" #include "exception-py.h" #include "result-py.h" #define LR_LOGDOMAIN "librepo" // Global variables GSList *logfiledata_list = NULL; G_LOCK_DEFINE(logfiledata_list_lock); typedef struct { long uid; gchar *fn; FILE *f; guint handler_id; } LogFileData; void logfiledata_free(LogFileData *data) { if (!data) return; g_log_remove_handler(LR_LOGDOMAIN, data->handler_id); fclose(data->f); g_free(data->fn); g_free(data); } void remove_all_log_handlers(void) { G_LOCK(logfiledata_list_lock); for (GSList *elem = logfiledata_list; elem; elem = g_slist_next(elem)) logfiledata_free((LogFileData *) elem->data); g_slist_free(logfiledata_list); logfiledata_list = NULL; G_UNLOCK(logfiledata_list_lock); } void logfile_func(G_GNUC_UNUSED const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level, const gchar *msg, gpointer user_data) { LogFileData *data = user_data; _cleanup_free_ gchar *time = NULL; _cleanup_date_time_unref_ GDateTime *datetime = NULL; datetime = g_date_time_new_now_local(); time = g_date_time_format(datetime, "%H:%M:%S"); fprintf(data->f, "%s %s\n", time, msg); fflush(data->f); } PyObject * py_log_set_file(G_GNUC_UNUSED PyObject *self, PyObject *args) { static long uid = 0; const char *fn = NULL; // Parse arguments if (!PyArg_ParseTuple(args, "s:py_log_set_file", &fn)) return NULL; // Open the file FILE *f = fopen(fn, "a"); if (!f) { PyErr_Format(PyExc_IOError, "Cannot open %s: %s", fn, g_strerror(errno)); return NULL; } // Setup user data LogFileData *data = g_malloc0(sizeof(*data)); data->fn = g_strdup(fn); data->f = f; // Set handler data->handler_id = g_log_set_handler(LR_LOGDOMAIN, G_LOG_LEVEL_DEBUG, logfile_func, data); // Save user data (in a thread safe way) G_LOCK(logfiledata_list_lock); // Get unique ID of the handler data->uid = ++uid; // Append the data to the global list logfiledata_list = g_slist_prepend(logfiledata_list, data); G_UNLOCK(logfiledata_list_lock); // Log librepo version and current time (including timezone) lr_log_librepo_summary(); // Return unique id of the handler data return PyLong_FromLong(data->uid); } PyObject * py_log_remove_handler(G_GNUC_UNUSED PyObject *self, PyObject *args) { long uid = -1; // Parse arguments if (!PyArg_ParseTuple(args, "l:py_log_remove_handler", &uid)) return NULL; // Lock the list G_LOCK(logfiledata_list_lock); // Search for the corresponding LogFileData LogFileData *data = NULL; for (GSList *elem = logfiledata_list; elem; elem = g_slist_next(elem)) { data = elem->data; if (data->uid == uid) break; } if (!data || data->uid != uid) { // No corresponding handler found G_UNLOCK(logfiledata_list_lock); PyErr_Format(LrErr_Exception, "Log handler with id %ld doesn't exist", uid); return NULL; } // Remove the item from the list logfiledata_list = g_slist_remove(logfiledata_list, data); // Unlock the list G_UNLOCK(logfiledata_list_lock); // Remove the handler and free the data logfiledata_free(data); Py_RETURN_NONE; } librepo-1.12.1/librepo/python/logging.h000066400000000000000000000023551372027637700200270ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2015 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_LOGGING_H__ #define __LR_LOGGING_H__ #include "librepo/librepo.h" /** Remove all log handlers set via bindings */ void remove_all_log_handlers(void); PyObject * py_log_set_file(G_GNUC_UNUSED PyObject *self, PyObject *args); PyObject * py_log_remove_handler(G_GNUC_UNUSED PyObject *self, PyObject *args); #endif librepo-1.12.1/librepo/python/metadatadownloader-py.c000066400000000000000000000053561372027637700226650ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2016 Martin Hatina * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "librepo/librepo.h" #include "metadatatarget-py.h" #include "metadatadownloader-py.h" #include "globalstate-py.h" #include "downloader-py.h" PyObject * py_download_metadata(PyObject *self, PyObject *args) { gboolean ret; PyObject *py_list; GError *error = NULL; PyThreadState *state = NULL; if (!PyArg_ParseTuple(args, "O!:download_metadata", &PyList_Type, &py_list)) return NULL; // Convert python list to GSList GSList *list = NULL; Py_ssize_t len = PyList_Size(py_list); for (Py_ssize_t x=0; x < len; x++) { PyObject *py_metadatatarget = PyList_GetItem(py_list, x); LrMetadataTarget *target = MetadataTarget_FromPyObject(py_metadatatarget); if (!target) return NULL; MetadataTarget_SetThreadState(py_metadatatarget, &state); list = g_slist_append(list, target); } Py_XINCREF(py_list); // XXX: GIL Hack int hack_rc = gil_logger_hack_begin(&state); if (hack_rc == GIL_HACK_ERROR) return NULL; BeginAllowThreads(&state); ret = lr_download_metadata(list, &error); EndAllowThreads(&state); // XXX: GIL Hack if (!gil_logger_hack_end(hack_rc)) return NULL; assert((ret && !error) || (!ret && error)); Py_XDECREF(py_list); if (ret) Py_RETURN_NONE; // All fine - Return None // Error occurred if (PyErr_Occurred()) { // Python exception occurred (in a python callback probably) return NULL; } else if(error->code == LRE_INTERRUPTED) { // Interrupted by Ctr+C g_error_free(error); PyErr_SetInterrupt(); PyErr_CheckSignals(); return NULL; } else { // Return exception created from GError RETURN_ERROR(&error, -1, NULL); } } librepo-1.12.1/librepo/python/metadatadownloader-py.h000066400000000000000000000021751372027637700226660ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2016 Martin Hatina * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef LIBREPO_METADATADOWNLOADER_PY_H #define LIBREPO_METADATADOWNLOADER_PY_H #include "librepo/librepo.h" PyObject *py_download_metadata(PyObject *self, PyObject *args); #endif //LIBREPO_METADATADOWNLOADER_PY_H librepo-1.12.1/librepo/python/metadatatarget-py.c000066400000000000000000000352471372027637700220170ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2016 Martin Hatina * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "librepo/librepo.h" #include "librepo/downloader_internal.h" #include "metadatatarget-py.h" #include "handle-py.h" #include "typeconversion.h" #include "exception-py.h" #include "downloader-py.h" typedef struct { PyObject_HEAD LrMetadataTarget *target; /* Handle */ PyObject *handle; /* Callback */ PyObject *cb_data; PyObject *progress_cb; PyObject *mirrorfailure_cb; PyObject *end_cb; /* GIL Stuff */ PyThreadState **state; } _MetadataTargetObject; LrMetadataTarget * MetadataTarget_FromPyObject(PyObject *o) { if (!MetadataTargetObject_Check(o)) { PyErr_SetString(PyExc_TypeError, "Expected a librepo.MetadataTarget object."); return NULL; } return ((_MetadataTargetObject *)o)->target; } void MetadataTarget_SetThreadState(PyObject *o, PyThreadState **state) { _MetadataTargetObject *self = (_MetadataTargetObject *) o; if (!self) return; self->state = state; if (self->handle) { Handle_SetThreadState(self->handle, state); } } static int check_MetadataTargetStatus(const _MetadataTargetObject *self) { assert(self != NULL); assert(MetadataTargetObject_Check(self)); if (self->target == NULL) { PyErr_SetString(LrErr_Exception, "No librepo target"); return -1; } return 0; } static int metadatatarget_progress_callback(void *data, double total_to_download, double now_downloaded) { int ret = LR_CB_OK; _MetadataTargetObject *self; PyObject *user_data, *result; LrCallbackData *callback_data = (LrCallbackData *) data; CbData *cbdata = (CbData *) callback_data->userdata; self = (_MetadataTargetObject *)cbdata->cbdata; if (!self || !self->progress_cb) return ret; if (self->cb_data) user_data = self->cb_data; else user_data = Py_None; EndAllowThreads(self->state); result = PyObject_CallFunction(self->progress_cb, "(Odd)", user_data, total_to_download, now_downloaded); if (!result) { ret = LR_CB_ERROR; } else { if (result == Py_None) { ret = LR_CB_OK; #if PY_MAJOR_VERSION < 3 } else if (PyInt_Check(result)) { ret = PyInt_AS_LONG(result); #endif } else if (PyLong_Check(result)) { ret = (int) PyLong_AsLong(result); } else { // It's an error if result is None neither int PyErr_SetString(PyExc_TypeError, "Progress callback must return integer number"); ret = LR_CB_ERROR; } } Py_XDECREF(result); BeginAllowThreads(self->state); return ret; } static int metadatatarget_mirrorfailure_callback(void *data, const char *msg, const char *url) { int ret = LR_CB_OK; // Assume everything will be ok _MetadataTargetObject *self; PyObject *user_data, *result, *py_msg, *py_url; LrCallbackData *callback_data = (LrCallbackData *) data; CbData *cbdata = (CbData *) callback_data->userdata; self = (_MetadataTargetObject *)cbdata->cbdata; if (!self->mirrorfailure_cb) return ret; if (self->cb_data) user_data = self->cb_data; else user_data = Py_None; EndAllowThreads(self->state); py_msg = PyStringOrNone_FromString(msg); py_url = PyStringOrNone_FromString(url); result = PyObject_CallFunction(self->mirrorfailure_cb, "(OOO)", user_data, py_msg, py_url); Py_DECREF(py_msg); Py_DECREF(py_url); if (!result) { // Exception raised in callback leads to the abortion // of whole downloading (it is considered fatal) ret = LR_CB_ERROR; } else { if (result == Py_None) { // Assume that None means that everything is ok ret = LR_CB_OK; #if PY_MAJOR_VERSION < 3 } else if (PyInt_Check(result)) { ret = PyInt_AS_LONG(result); #endif } else if (PyLong_Check(result)) { ret = (int) PyLong_AsLong(result); } else { // It's an error if result is None neither int PyErr_SetString(PyExc_TypeError, "Mirror failure callback must return integer number"); ret = LR_CB_ERROR; } } Py_XDECREF(result); BeginAllowThreads(self->state); return ret; } static int metadatatarget_end_callback(void *data, LrTransferStatus status, const char *msg) { int ret = LR_CB_OK; // Assume everything will be ok _MetadataTargetObject *self; PyObject *user_data, *result, *py_msg; LrCallbackData *callback_data = (LrCallbackData *) data; CbData *cbdata = (CbData *) callback_data->userdata; self = (_MetadataTargetObject *)cbdata->cbdata; LrMetadataTarget *target = self->target; target->repomd_records_downloaded++; if (target->repomd_records_to_download != target->repomd_records_downloaded) return ret; else if (!self->end_cb) return ret; if (self->cb_data) user_data = self->cb_data; else user_data = Py_None; EndAllowThreads(self->state); py_msg = PyStringOrNone_FromString(msg); result = PyObject_CallFunction(self->end_cb, "(OiO)", user_data, status, py_msg); Py_DECREF(py_msg); if (!result) { // Exception raised in callback leads to the abortion // of whole downloading (it is considered fatal) ret = LR_CB_ERROR; } else { if (result == Py_None) { // Assume that None means that everything is ok ret = LR_CB_OK; #if PY_MAJOR_VERSION < 3 } else if (PyInt_Check(result)) { ret = PyInt_AS_LONG(result); #endif } else if (PyLong_Check(result)) { ret = (int) PyLong_AsLong(result); } else { // It's an error if result is None neither int PyErr_SetString(PyExc_TypeError, "End callback must return integer number"); ret = LR_CB_ERROR; } } Py_XDECREF(result); BeginAllowThreads(self->state); return ret; } static PyObject * metadatatarget_new(PyTypeObject *type, G_GNUC_UNUSED PyObject *args, G_GNUC_UNUSED PyObject *kwds) { _MetadataTargetObject *self = (_MetadataTargetObject *)type->tp_alloc(type, 0); if (self) { self->target = NULL; self->handle = NULL; self->cb_data = NULL; self->progress_cb = NULL; self->mirrorfailure_cb = NULL; self->end_cb = NULL; self->state = NULL; } return (PyObject *)self; } static int metadatatarget_init(_MetadataTargetObject *self, PyObject *args, PyObject *kwds G_GNUC_UNUSED) { const char *gnupghomedir; PyObject *pyhandle, *py_cbdata; PyObject *py_endcb, *py_progresscb; PyObject *py_mirrorfailurecb; LrEndCb endcb = NULL; LrHandle *handle = NULL; LrProgressCb progresscb = NULL; LrMirrorFailureCb mirrorfailurecb = NULL; GError *tmp_err = NULL; if (!PyArg_ParseTuple(args, "OOOOOs:metadatatarget_init", &pyhandle, &py_cbdata, &py_progresscb, &py_mirrorfailurecb, &py_endcb, &gnupghomedir)) return -1; if (pyhandle != Py_None) { handle = Handle_FromPyObject(pyhandle); if (!handle) return -1; self->handle = pyhandle; Py_INCREF(self->handle); } if (!PyCallable_Check(py_progresscb) && py_progresscb != Py_None) { PyErr_SetString(PyExc_TypeError, "progresscb must be callable or None"); return -1; } if (!PyCallable_Check(py_mirrorfailurecb) && py_mirrorfailurecb != Py_None) { PyErr_SetString(PyExc_TypeError, "mirrorfailurecb must be callable or None"); return -1; } if (!PyCallable_Check(py_endcb) && py_endcb != Py_None) { PyErr_SetString(PyExc_TypeError, "endcb must be callable or None"); return -1; } if (py_cbdata) { self->cb_data = py_cbdata; Py_XINCREF(self->cb_data); } if (py_progresscb != Py_None) { progresscb = metadatatarget_progress_callback; self->progress_cb = py_progresscb; Py_XINCREF(self->progress_cb); } if (py_mirrorfailurecb != Py_None) { mirrorfailurecb = metadatatarget_mirrorfailure_callback; self->mirrorfailure_cb = py_mirrorfailurecb; Py_XINCREF(self->mirrorfailure_cb); } if (py_endcb != Py_None) { endcb = metadatatarget_end_callback; self->end_cb = py_endcb; Py_XINCREF(self->end_cb); } self->target = lr_metadatatarget_new2(handle, self, progresscb, mirrorfailurecb, endcb, gnupghomedir, &tmp_err); if (self->target == NULL) { PyErr_Format(LrErr_Exception, "MetadataTarget initialization failed: %s", tmp_err->message); g_error_free(tmp_err); return -1; } return 0; } static void metadatatarget_dealloc(_MetadataTargetObject *o) { if (o->target) lr_metadatatarget_free(o->target); Py_XDECREF(o->progress_cb); Py_XDECREF(o->mirrorfailure_cb); Py_XDECREF(o->cb_data); Py_XDECREF(o->handle); Py_TYPE(o)->tp_free(o); } // Get/setters #define OFFSET(member) (void *) offsetof(LrMetadataTarget, member) static PyObject * get_pythonobj(_MetadataTargetObject *self, void *member_offset) { if (check_MetadataTargetStatus(self)) return NULL; if (member_offset == OFFSET(handle)) { if (!self->handle) Py_RETURN_NONE; Py_XINCREF(self->handle); return self->handle; } if (member_offset == OFFSET(cbdata)) { if (!self->cb_data) Py_RETURN_NONE; Py_XINCREF(self->cb_data); return self->cb_data; } if (member_offset == OFFSET(progresscb)) { if (!self->progress_cb) Py_RETURN_NONE; Py_XINCREF(self->progress_cb); return self->progress_cb; } if (member_offset == OFFSET(endcb)) { if (!self->end_cb) Py_RETURN_NONE; Py_XINCREF(self->end_cb); return self->end_cb; } if (member_offset == OFFSET(mirrorfailurecb)) { if (!self->mirrorfailure_cb) Py_RETURN_NONE; Py_XINCREF(self->mirrorfailure_cb); return self->mirrorfailure_cb; } if (member_offset == OFFSET(err)) { LrMetadataTarget *target = self->target; if (!target->err) Py_RETURN_NONE; int i = 0; PyObject *pylist = PyTuple_New(g_list_length(target->err)); for (GList *elem = target->err; elem; i++, elem = g_list_next(elem)) { gchar *error_message = (gchar *) elem->data; PyObject *str = PyStringOrNone_FromString(error_message); PyTuple_SetItem(pylist, i, str); } Py_XINCREF(target->err); return pylist; } Py_RETURN_NONE; } static PyGetSetDef metadatatarget_getsetters[] = { {"handle", (getter)get_pythonobj, NULL, NULL, OFFSET(handle)}, {"cbdata", (getter)get_pythonobj, NULL, NULL, OFFSET(cbdata)}, {"progresscb", (getter)get_pythonobj, NULL, NULL, OFFSET(progresscb)}, {"mirrorfailurecb",(getter)get_pythonobj,NULL, NULL, OFFSET(mirrorfailurecb)}, {"endcb", (getter)get_pythonobj, NULL, NULL, OFFSET(endcb)}, {"err", (getter)get_pythonobj, NULL, NULL, OFFSET(err)}, {NULL, NULL, NULL, NULL, NULL} /* sentinel */ }; static struct PyMethodDef metadatatarget_methods[] = { { NULL } }; PyTypeObject MetadataTarget_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_librepo.MetadataTarget", /* tp_name */ sizeof(_MetadataTargetObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor) metadatatarget_dealloc,/* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,/* tp_flags */ "MetadataTarget object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ 0, /* tp_iternext */ metadatatarget_methods, /* tp_methods */ 0, /* tp_members */ metadatatarget_getsetters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc) metadatatarget_init, /* tp_init */ 0, /* tp_alloc */ metadatatarget_new, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ }; librepo-1.12.1/librepo/python/metadatatarget-py.h000066400000000000000000000025111372027637700220100ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2016 Martin Hatina * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef LIBREPO_METADATATARGET_PY_H #define LIBREPO_METADATATARGET_PY_H #include #include extern PyTypeObject MetadataTarget_Type; #define MetadataTargetObject_Check(o) PyObject_TypeCheck(o, &MetadataTarget_Type) LrMetadataTarget *MetadataTarget_FromPyObject(PyObject *o); void MetadataTarget_SetThreadState(PyObject *o, PyThreadState **state); #endif //LIBREPO_METADATATARGET_PY_H librepo-1.12.1/librepo/python/packagedownloader-py.c000066400000000000000000000057311372027637700224750ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #undef NDEBUG #include #include "librepo/librepo.h" #include "packagedownloader-py.h" #include "handle-py.h" #include "packagetarget-py.h" #include "exception-py.h" #include "downloader-py.h" #include "globalstate-py.h" // GIL Hack PyObject * py_download_packages(G_GNUC_UNUSED PyObject *self, PyObject *args) { gboolean ret; PyObject *py_list; int failfast; LrPackageDownloadFlag flags = 0; GError *tmp_err = NULL; PyThreadState *state = NULL; if (!PyArg_ParseTuple(args, "O!i:download_packages", &PyList_Type, &py_list, &failfast)) return NULL; // Convert python list to GSList GSList *list = NULL; Py_ssize_t len = PyList_Size(py_list); for (Py_ssize_t x=0; x < len; x++) { PyObject *py_packagetarget = PyList_GetItem(py_list, x); LrPackageTarget *target = PackageTarget_FromPyObject(py_packagetarget); if (!target) return NULL; PackageTarget_SetThreadState(py_packagetarget, &state); list = g_slist_append(list, target); } Py_XINCREF(py_list); if (failfast) flags |= LR_PACKAGEDOWNLOAD_FAILFAST; // XXX: GIL Hack int hack_rc = gil_logger_hack_begin(&state); if (hack_rc == GIL_HACK_ERROR) return NULL; BeginAllowThreads(&state); ret = lr_download_packages(list, flags, &tmp_err); EndAllowThreads(&state); // XXX: GIL Hack if (!gil_logger_hack_end(hack_rc)) return NULL; assert((ret && !tmp_err) || (!ret && tmp_err)); Py_XDECREF(py_list); if (ret) Py_RETURN_NONE; // All fine - Return None // Error occurred if (PyErr_Occurred()) { // Python exception occurred (in a python callback probably) return NULL; } else if(tmp_err->code == LRE_INTERRUPTED) { // Interrupted by Ctr+C g_error_free(tmp_err); PyErr_SetInterrupt(); PyErr_CheckSignals(); return NULL; } else { // Return exception created from GError RETURN_ERROR(&tmp_err, -1, NULL); } } librepo-1.12.1/librepo/python/packagedownloader-py.h000066400000000000000000000021261372027637700224750ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_PACKAGEDOWNLOADER_PY_H__ #define __LR_PACKAGEDOWNLOADER_PY_H__ #include "librepo/librepo.h" PyObject *py_download_packages(PyObject *self, PyObject *args); #endif librepo-1.12.1/librepo/python/packagetarget-py.c000066400000000000000000000405441372027637700216260ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #undef NDEBUG #include #include "librepo/librepo.h" #include "handle-py.h" #include "packagetarget-py.h" #include "exception-py.h" #include "downloader-py.h" #include "packagedownloader-py.h" #include "typeconversion.h" typedef struct { PyObject_HEAD LrPackageTarget *target; /* Handle */ PyObject *handle; /* Callback */ PyObject *cb_data; PyObject *progress_cb; PyObject *end_cb; PyObject *mirrorfailure_cb; /* GIL Stuff */ PyThreadState **state; } _PackageTargetObject; LrPackageTarget * PackageTarget_FromPyObject(PyObject *o) { if (!PackageTargetObject_Check(o)) { PyErr_SetString(PyExc_TypeError, "Expected a librepo.PackageTarget object."); return NULL; } return ((_PackageTargetObject *)o)->target; } static int check_PackageTargetStatus(const _PackageTargetObject *self) { assert(self != NULL); assert(PackageTargetObject_Check(self)); if (self->target == NULL) { PyErr_SetString(LrErr_Exception, "No librepo target"); return -1; } return 0; } /* Callback stuff */ static int packagetarget_progress_callback(void *data, double total_to_download, double now_downloaded) { int ret = LR_CB_OK; // Assume everything will be ok _PackageTargetObject *self; PyObject *user_data, *result; self = (_PackageTargetObject *)data; if (!self->progress_cb) return ret; if (self->cb_data) user_data = self->cb_data; else user_data = Py_None; EndAllowThreads(self->state); result = PyObject_CallFunction(self->progress_cb, "(Odd)", user_data, total_to_download, now_downloaded); if (!result) { // Exception raised in callback leads to the abortion // of whole downloading (it is considered fatal) ret = LR_CB_ERROR; } else { if (result == Py_None) { // Assume that None means that everything is ok ret = LR_CB_OK; #if PY_MAJOR_VERSION < 3 } else if (PyInt_Check(result)) { ret = PyInt_AS_LONG(result); #endif } else if (PyLong_Check(result)) { ret = (int) PyLong_AsLong(result); } else { // It's an error if result is None neither int PyErr_SetString(PyExc_TypeError, "Progress callback must return integer number"); ret = LR_CB_ERROR; } } Py_XDECREF(result); BeginAllowThreads(self->state); return ret; } static int packagetarget_end_callback(void *data, LrTransferStatus status, const char *msg) { int ret = LR_CB_OK; // Assume everything will be ok _PackageTargetObject *self; PyObject *user_data, *result, *py_msg; self = (_PackageTargetObject *)data; if (!self->end_cb) return ret; if (self->cb_data) user_data = self->cb_data; else user_data = Py_None; EndAllowThreads(self->state); py_msg = PyStringOrNone_FromString(msg); result = PyObject_CallFunction(self->end_cb, "(OiO)", user_data, status, py_msg); Py_DECREF(py_msg); if (!result) { // Exception raised in callback leads to the abortion // of whole downloading (it is considered fatal) ret = LR_CB_ERROR; } else { if (result == Py_None) { // Assume that None means that everything is ok ret = LR_CB_OK; #if PY_MAJOR_VERSION < 3 } else if (PyInt_Check(result)) { ret = PyInt_AS_LONG(result); #endif } else if (PyLong_Check(result)) { ret = (int) PyLong_AsLong(result); } else { // It's an error if result is None neither int PyErr_SetString(PyExc_TypeError, "End callback must return integer number"); ret = LR_CB_ERROR; } } Py_XDECREF(result); BeginAllowThreads(self->state); return ret; } static int packagetarget_mirrorfailure_callback(void *data, const char *msg, const char *url) { int ret = LR_CB_OK; // Assume everything will be ok _PackageTargetObject *self; PyObject *user_data, *result, *py_msg, *py_url; self = (_PackageTargetObject *)data; if (!self->mirrorfailure_cb) return ret; if (self->cb_data) user_data = self->cb_data; else user_data = Py_None; EndAllowThreads(self->state); py_msg = PyStringOrNone_FromString(msg); py_url = PyStringOrNone_FromString(url); result = PyObject_CallFunction(self->mirrorfailure_cb, "(OOO)", user_data, py_msg, py_url); Py_DECREF(py_msg); Py_DECREF(py_url); if (!result) { // Exception raised in callback leads to the abortion // of whole downloading (it is considered fatal) ret = LR_CB_ERROR; } else { if (result == Py_None) { // Assume that None means that everything is ok ret = LR_CB_OK; #if PY_MAJOR_VERSION < 3 } else if (PyInt_Check(result)) { ret = PyInt_AS_LONG(result); #endif } else if (PyLong_Check(result)) { ret = (int) PyLong_AsLong(result); } else { // It's an error if result is None neither int PyErr_SetString(PyExc_TypeError, "Mirror failure callback must return integer number"); ret = LR_CB_ERROR; } } Py_XDECREF(result); BeginAllowThreads(self->state); return ret; } void PackageTarget_SetThreadState(PyObject *o, PyThreadState **state) { _PackageTargetObject *self = (_PackageTargetObject *) o; if (!self) return; self->state = state; // XXX: Little tricky (but not so much) // Set the state to the depending handle as well // (Needed when fastestmirrorcb is used) if (self->handle) { Handle_SetThreadState(self->handle, state); } } /* Function on the type */ static PyObject * packagetarget_new(PyTypeObject *type, G_GNUC_UNUSED PyObject *args, G_GNUC_UNUSED PyObject *kwds) { _PackageTargetObject *self = (_PackageTargetObject *)type->tp_alloc(type, 0); if (self) { self->target = NULL; self->handle = NULL; self->cb_data = NULL; self->progress_cb = NULL; self->end_cb = NULL; self->mirrorfailure_cb = NULL; self->state = NULL; } return (PyObject *)self; } static int packagetarget_init(_PackageTargetObject *self, PyObject *args, PyObject *kwds G_GNUC_UNUSED) { char *relative_url, *dest, *checksum, *base_url; int checksum_type, resume; PY_LONG_LONG expectedsize, byterangestart, byterangeend; PyObject *pyhandle, *py_progresscb, *py_cbdata; PyObject *py_endcb, *py_mirrorfailurecb; LrProgressCb progresscb = NULL; LrEndCb endcb = NULL; LrMirrorFailureCb mirrorfailurecb = NULL; LrHandle *handle = NULL; GError *tmp_err = NULL; PyObject *py_dest = NULL; PyObject *tmp_py_str = NULL; if (!PyArg_ParseTuple(args, "OsOizLziOOOOLL:packagetarget_init", &pyhandle, &relative_url, &py_dest, &checksum_type, &checksum, &expectedsize, &base_url, &resume, &py_progresscb, &py_cbdata, &py_endcb, &py_mirrorfailurecb, &byterangestart, &byterangeend)) return -1; dest = PyAnyStr_AsString(py_dest, &tmp_py_str); if (pyhandle != Py_None) { handle = Handle_FromPyObject(pyhandle); if (!handle) return -1; self->handle = pyhandle; Py_INCREF(self->handle); } if (!PyCallable_Check(py_progresscb) && py_progresscb != Py_None) { PyErr_SetString(PyExc_TypeError, "progresscb must be callable or None"); return -1; } if (!PyCallable_Check(py_endcb) && py_endcb != Py_None) { PyErr_SetString(PyExc_TypeError, "endcb must be callable or None"); return -1; } if (!PyCallable_Check(py_mirrorfailurecb) && py_mirrorfailurecb != Py_None) { PyErr_SetString(PyExc_TypeError, "mirrorfailurecb must be callable or None"); return -1; } if (py_cbdata) { self->cb_data = py_cbdata; Py_XINCREF(self->cb_data); } if (py_progresscb != Py_None) { progresscb = packagetarget_progress_callback; self->progress_cb = py_progresscb; Py_XINCREF(self->progress_cb); } if (py_endcb != Py_None) { endcb = packagetarget_end_callback; self->end_cb = py_endcb; Py_XINCREF(self->end_cb); } if (py_mirrorfailurecb != Py_None) { mirrorfailurecb = packagetarget_mirrorfailure_callback; self->mirrorfailure_cb = py_mirrorfailurecb; Py_XINCREF(self->mirrorfailure_cb); } if (resume && byterangestart) { PyErr_SetString(PyExc_TypeError, "resume cannot be used simultaneously " "with the byterangestart param"); return -1; } self->target = lr_packagetarget_new_v3(handle, relative_url, dest, checksum_type, checksum, (gint64) expectedsize, base_url, resume, progresscb, self, endcb, mirrorfailurecb, (gint64) byterangestart, (gint64) byterangeend, &tmp_err); Py_XDECREF(tmp_py_str); if (self->target == NULL) { PyErr_Format(LrErr_Exception, "PackageTarget initialization failed: %s", tmp_err->message); g_error_free(tmp_err); return -1; } return 0; } static void packagetarget_dealloc(_PackageTargetObject *o) { if (o->target) lr_packagetarget_free(o->target); Py_XDECREF(o->cb_data); Py_XDECREF(o->progress_cb); Py_XDECREF(o->end_cb); Py_XDECREF(o->mirrorfailure_cb); Py_XDECREF(o->handle); Py_TYPE(o)->tp_free(o); } static struct PyMethodDef packagetarget_methods[] = { { NULL } }; // Get/setters #define OFFSET(member) (void *) offsetof(LrPackageTarget, member) static PyObject * get_gint64(_PackageTargetObject *self, void *member_offset) { if (check_PackageTargetStatus(self)) return NULL; LrPackageTarget *target = self->target; gint64 val = *((gint64 *) ((size_t)target + (size_t) member_offset)); return PyLong_FromLongLong((PY_LONG_LONG) val); } static PyObject * get_int(_PackageTargetObject *self, void *member_offset) { if (check_PackageTargetStatus(self)) return NULL; LrPackageTarget *target = self->target; int val = *((int *) ((size_t)target + (size_t) member_offset)); return PyLong_FromLong((long) val); } static PyObject * get_str(_PackageTargetObject *self, void *member_offset) { if (check_PackageTargetStatus(self)) return NULL; LrPackageTarget *target = self->target; char *str = *((char **) ((size_t) target + (size_t) member_offset)); if (str == NULL) Py_RETURN_NONE; return PyStringOrNone_FromString(str); } static PyObject * get_pythonobj(_PackageTargetObject *self, void *member_offset) { if (check_PackageTargetStatus(self)) return NULL; if (member_offset == OFFSET(handle)) { if (!self->handle) Py_RETURN_NONE; Py_XINCREF(self->handle); return self->handle; } if (member_offset == OFFSET(cbdata)) { if (!self->cb_data) Py_RETURN_NONE; Py_XINCREF(self->cb_data); return self->cb_data; } if (member_offset == OFFSET(progresscb)) { if (!self->progress_cb) Py_RETURN_NONE; Py_XINCREF(self->progress_cb); return self->progress_cb; } if (member_offset == OFFSET(endcb)) { if (!self->end_cb) Py_RETURN_NONE; Py_XINCREF(self->end_cb); return self->end_cb; } if (member_offset == OFFSET(mirrorfailurecb)) { if (!self->mirrorfailure_cb) Py_RETURN_NONE; Py_XINCREF(self->mirrorfailure_cb); return self->mirrorfailure_cb; } Py_RETURN_NONE; } static PyGetSetDef packagetarget_getsetters[] = { {"handle", (getter)get_pythonobj, NULL, NULL, OFFSET(handle)}, {"relative_url", (getter)get_str, NULL, NULL, OFFSET(relative_url)}, {"dest", (getter)get_str, NULL, NULL, OFFSET(dest)}, {"base_url", (getter)get_str, NULL, NULL, OFFSET(base_url)}, {"checksum_type", (getter)get_int, NULL, NULL, OFFSET(checksum_type)}, {"checksum", (getter)get_str, NULL, NULL, OFFSET(checksum)}, {"expectedsize", (getter)get_gint64, NULL, NULL, OFFSET(expectedsize)}, {"resume", (getter)get_int, NULL, NULL, OFFSET(resume)}, {"cbdata", (getter)get_pythonobj, NULL, NULL, OFFSET(cbdata)}, {"progresscb", (getter)get_pythonobj, NULL, NULL, OFFSET(progresscb)}, {"endcb", (getter)get_pythonobj, NULL, NULL, OFFSET(endcb)}, {"mirrorfailurecb",(getter)get_pythonobj,NULL, NULL, OFFSET(mirrorfailurecb)}, {"local_path", (getter)get_str, NULL, NULL, OFFSET(local_path)}, {"err", (getter)get_str, NULL, NULL, OFFSET(err)}, {NULL, NULL, NULL, NULL, NULL} /* sentinel */ }; // Object definition PyTypeObject PackageTarget_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_librepo.PackageTarget", /* tp_name */ sizeof(_PackageTargetObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor) packagetarget_dealloc,/* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,/* tp_flags */ "PackageTarget object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ 0, /* tp_iternext */ packagetarget_methods, /* tp_methods */ 0, /* tp_members */ packagetarget_getsetters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc) packagetarget_init, /* tp_init */ 0, /* tp_alloc */ packagetarget_new, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ }; librepo-1.12.1/librepo/python/packagetarget-py.h000066400000000000000000000024131372027637700216240ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_PACKAGETARGET_PY_H__ #define __LR_PACKAGETARGET_PY_H__ #include "librepo/librepo.h" extern PyTypeObject PackageTarget_Type; #define PackageTargetObject_Check(o) PyObject_TypeCheck(o, &PackageTarget_Type) LrPackageTarget *PackageTarget_FromPyObject(PyObject *o); void PackageTarget_SetThreadState(PyObject *o, PyThreadState **state); #endif librepo-1.12.1/librepo/python/python2/000077500000000000000000000000001372027637700176265ustar00rootroot00000000000000librepo-1.12.1/librepo/python/python2/CMakeLists.txt000066400000000000000000000023541372027637700223720ustar00rootroot00000000000000EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c " from sys import stdout from distutils import sysconfig path=sysconfig.get_python_lib(True, prefix='${CMAKE_INSTALL_PREFIX}') stdout.write(path)" OUTPUT_VARIABLE PYTHON_INSTALL_DIR) INCLUDE_DIRECTORIES (${PYTHON_INCLUDE_PATH}) MESSAGE(STATUS "Python install dir is ${PYTHON_INSTALL_DIR}") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-strict-aliasing") set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-strict-aliasing") set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fno-strict-aliasing") SET (librepo_COPIES ${pylibrepo_SRCDIR}/__init__.py) FILE(COPY ${librepo_COPIES} DESTINATION ./librepo/) ADD_LIBRARY(_librepomodule SHARED ${librepomodule_SRCS}) SET_TARGET_PROPERTIES(_librepomodule PROPERTIES PREFIX "") SET_TARGET_PROPERTIES(_librepomodule PROPERTIES LIBRARY_OUTPUT_DIRECTORY "./librepo") TARGET_LINK_LIBRARIES(_librepomodule librepo) TARGET_LINK_LIBRARIES(_librepomodule ${EXPAT_LIBRARIES} ${CURL_LIBRARY} ${PYTHON_LIBRARY} ) INSTALL(FILES ${pylibrepo_SRCDIR}/__init__.py DESTINATION ${PYTHON_INSTALL_DIR}/librepo) INSTALL(TARGETS _librepomodule LIBRARY DESTINATION ${PYTHON_INSTALL_DIR}/librepo) librepo-1.12.1/librepo/python/python3/000077500000000000000000000000001372027637700176275ustar00rootroot00000000000000librepo-1.12.1/librepo/python/python3/CMakeLists.txt000066400000000000000000000023111372027637700223640ustar00rootroot00000000000000EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c " from sys import stdout from distutils import sysconfig path=sysconfig.get_python_lib(True, prefix='${CMAKE_INSTALL_PREFIX}') stdout.write(path)" OUTPUT_VARIABLE PYTHON_INSTALL_DIR) INCLUDE_DIRECTORIES (${PYTHON_INCLUDE_PATH}) MESSAGE(STATUS "Python3 install dir is ${PYTHON_INSTALL_DIR}") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-strict-aliasing") set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-strict-aliasing") set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fno-strict-aliasing") SET (librepo_COPIES ${pylibrepo_SRCDIR}/__init__.py) FILE(COPY ${librepo_COPIES} DESTINATION ./librepo/) ADD_LIBRARY(_librepo SHARED ${librepomodule_SRCS}) SET_TARGET_PROPERTIES(_librepo PROPERTIES PREFIX "") SET_TARGET_PROPERTIES(_librepo PROPERTIES LIBRARY_OUTPUT_DIRECTORY "./librepo") TARGET_LINK_LIBRARIES(_librepo librepo) TARGET_LINK_LIBRARIES(_librepo ${EXPAT_LIBRARIES} ${CURL_LIBRARY} ${PYTHON_LIBRARY} ) INSTALL(FILES ${pylibrepo_SRCDIR}/__init__.py DESTINATION ${PYTHON_INSTALL_DIR}/librepo) INSTALL(TARGETS _librepo LIBRARY DESTINATION ${PYTHON_INSTALL_DIR}/librepo) librepo-1.12.1/librepo/python/result-py.c000066400000000000000000000170241372027637700203370ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #undef NDEBUG #include #include "librepo/librepo.h" #include "typeconversion.h" #include "exception-py.h" #include "result-py.h" typedef struct { PyObject_HEAD LrResult *result; } _ResultObject; LrResult * Result_FromPyObject(PyObject *o) { if (!ResultObject_Check(o)) { PyErr_SetString(PyExc_TypeError, "Expected a _librepo.Result object."); return NULL; } return ((_ResultObject *)o)->result; } /* int result_converter(PyObject *o, LrHandle *handle_ptr) { LrHandle handle = handleFromPyObject(o); if (handle == NULL) return 0; *handle_ptr = handle; return 1; } */ static int check_ResultStatus(const _ResultObject *self) { assert(self != NULL); assert(ResultObject_Check(self)); if (self->result == NULL) { PyErr_SetString(LrErr_Exception, "No librepo result"); return -1; } return 0; } /* Function on the type */ static PyObject * result_new(PyTypeObject *type, G_GNUC_UNUSED PyObject *args, G_GNUC_UNUSED PyObject *kwds) { _ResultObject *self = (_ResultObject *)type->tp_alloc(type, 0); if (self) self->result = NULL; return (PyObject *)self; } static int result_init(_ResultObject *self, PyObject *args, PyObject *kwds) { char *kwlist[] = {NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|", kwlist)) return -1; self->result = lr_result_init(); if (self->result == NULL) { PyErr_SetString(LrErr_Exception, "Result initialization failed"); return -1; } return 0; } static void result_dealloc(_ResultObject *o) { if (o->result) lr_result_free(o->result); Py_TYPE(o)->tp_free(o); } static PyObject * getinfo(_ResultObject *self, PyObject *args) { int option; gboolean res = TRUE; if (!PyArg_ParseTuple(args, "i:getinfo", &option)) return NULL; if (check_ResultStatus(self)) return NULL; switch (option) { /* * YUM related options */ case LRR_YUM_REPO: { LrYumRepo *repo; GError *tmp_err = NULL; res = lr_result_getinfo(self->result, &tmp_err, (LrResultInfoOption)option, &repo); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); return PyObject_FromYumRepo(repo); } case LRR_YUM_REPOMD: { LrYumRepoMd *repomd; GError *tmp_err = NULL; res = lr_result_getinfo(self->result, &tmp_err, (LrResultInfoOption)option, &repomd); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); return PyObject_FromYumRepoMd(repomd); } case LRR_RPMMD_TIMESTAMP: case LRR_YUM_TIMESTAMP: { gint64 ts; GError *tmp_err = NULL; res = lr_result_getinfo(self->result, &tmp_err, (LrResultInfoOption)option, &ts); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); return PyLong_FromLongLong((PY_LONG_LONG) ts); } case LRR_RPMMD_REPO: { LrYumRepo *repo; GError *tmp_err = NULL; res = lr_result_getinfo(self->result, &tmp_err, (LrResultInfoOption)option, &repo); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); return PyObject_FromYumRepo_v2(repo); } case LRR_RPMMD_REPOMD: { LrYumRepoMd *repomd; GError *tmp_err = NULL; res = lr_result_getinfo(self->result, &tmp_err, (LrResultInfoOption)option, &repomd); if (!res) RETURN_ERROR(&tmp_err, -1, NULL); PyObject *obj = PyObject_FromYumRepoMd_v2(repomd); return obj; } /* * Unknown options */ default: PyErr_Format(PyExc_ValueError, "Unknown option (%d)", option); return NULL; } assert(res); Py_RETURN_NONE; } static PyObject * clear(_ResultObject *self, G_GNUC_UNUSED PyObject *noarg) { if (check_ResultStatus(self)) return NULL; lr_result_clear(self->result); Py_RETURN_NONE; } static struct PyMethodDef result_methods[] = { { "getinfo", (PyCFunction)getinfo, METH_VARARGS, NULL }, { "clear", (PyCFunction)clear, METH_NOARGS, NULL }, { NULL } }; PyTypeObject Result_Type = { PyVarObject_HEAD_INIT(NULL, 0) "_librepo.Result", /* tp_name */ sizeof(_ResultObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor) result_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ "Result object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ 0, /* tp_iternext */ result_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc) result_init, /* tp_init */ 0, /* tp_alloc */ result_new, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ }; librepo-1.12.1/librepo/python/result-py.h000066400000000000000000000022231372027637700203370ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_RESULT_PY_H__ #define __LR_RESULT_PY_H__ #include "librepo/librepo.h" extern PyTypeObject Result_Type; #define ResultObject_Check(o) PyObject_TypeCheck(o, &Result_Type) LrResult *Result_FromPyObject(PyObject *o); #endif librepo-1.12.1/librepo/python/typeconversion.c000066400000000000000000000327211372027637700214630ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "typeconversion.h" PyObject * PyStringOrNone_FromString(const char *str) { if (str == NULL) Py_RETURN_NONE; return PyUnicode_FromString(str); } /** * Set an object to a dict and decref its ref count. */ static int PyDict_SetItemStringAndDecref(PyObject *p, const char *key, PyObject *val) { int ret = PyDict_SetItemString(p, key, val); Py_XDECREF(val); return ret; } /** * bytes, basic string or unicode string in Python 2/3 to c string converter, * you need to call Py_XDECREF(tmp_py_str) after usage of returned string */ char * PyAnyStr_AsString(PyObject *str, PyObject **tmp_py_str) { char *res = NULL; if (PyUnicode_Check(str)) { *tmp_py_str = PyUnicode_AsUTF8String(str); res = PyBytes_AsString(*tmp_py_str); } #if PY_MAJOR_VERSION < 3 else if (PyString_Check(str)) res = PyString_AsString(str); #else else if (PyBytes_Check(str)) res = PyBytes_AsString(str); #endif return res; } PyObject * PyObject_FromYumRepo(LrYumRepo *repo) { PyObject *dict; if (!repo) Py_RETURN_NONE; if ((dict = PyDict_New()) == NULL) return NULL; PyDict_SetItemStringAndDecref(dict, "repomd", PyStringOrNone_FromString(repo->repomd)); PyDict_SetItemStringAndDecref(dict, "url", PyStringOrNone_FromString(repo->url)); PyDict_SetItemStringAndDecref(dict, "destdir", PyStringOrNone_FromString(repo->destdir)); PyDict_SetItemStringAndDecref(dict, "signature", PyStringOrNone_FromString(repo->signature)); PyDict_SetItemStringAndDecref(dict, "mirrorlist", PyStringOrNone_FromString(repo->mirrorlist)); PyDict_SetItemStringAndDecref(dict, "metalink", PyStringOrNone_FromString(repo->metalink)); for (GSList *elem = repo->paths; elem; elem = g_slist_next(elem)) { LrYumRepoPath *yumrepopath = elem->data; if (!yumrepopath || !yumrepopath->type) continue; PyDict_SetItemStringAndDecref(dict, yumrepopath->type, PyStringOrNone_FromString(yumrepopath->path)); } return dict; } PyObject * PyObject_FromYumRepo_v2(LrYumRepo *repo) { PyObject *dict, *paths; if (!repo) Py_RETURN_NONE; if ((dict = PyDict_New()) == NULL) return NULL; PyDict_SetItemStringAndDecref(dict, "repomd", PyStringOrNone_FromString(repo->repomd)); PyDict_SetItemStringAndDecref(dict, "url", PyStringOrNone_FromString(repo->url)); PyDict_SetItemStringAndDecref(dict, "destdir", PyStringOrNone_FromString(repo->destdir)); PyDict_SetItemStringAndDecref(dict, "signature", PyStringOrNone_FromString(repo->signature)); PyDict_SetItemStringAndDecref(dict, "mirrorlist", PyStringOrNone_FromString(repo->mirrorlist)); PyDict_SetItemStringAndDecref(dict, "metalink", PyStringOrNone_FromString(repo->metalink)); if ((paths = PyDict_New()) == NULL) return NULL; for (GSList *elem = repo->paths; elem; elem = g_slist_next(elem)) { LrYumRepoPath *yumrepopath = elem->data; if (!yumrepopath || !yumrepopath->type) continue; PyDict_SetItemStringAndDecref(paths, yumrepopath->type, PyStringOrNone_FromString(yumrepopath->path)); } PyDict_SetItemStringAndDecref(dict, "paths", paths); return dict; } PyObject * PyObject_FromRepoMdRecord(LrYumRepoMdRecord *rec) { PyObject *dict; if (!rec) Py_RETURN_NONE; if ((dict = PyDict_New()) == NULL) return NULL; PyDict_SetItemStringAndDecref(dict, "location_href", PyStringOrNone_FromString(rec->location_href)); PyDict_SetItemStringAndDecref(dict, "checksum", PyStringOrNone_FromString(rec->checksum)); PyDict_SetItemStringAndDecref(dict, "checksum_type", PyStringOrNone_FromString(rec->checksum_type)); PyDict_SetItemStringAndDecref(dict, "checksum_open", PyStringOrNone_FromString(rec->checksum_open)); PyDict_SetItemStringAndDecref(dict, "checksum_open_type", PyStringOrNone_FromString(rec->checksum_open_type)); PyDict_SetItemStringAndDecref(dict, "header_checksum", PyStringOrNone_FromString(rec->header_checksum)); PyDict_SetItemStringAndDecref(dict, "header_checksum_type", PyStringOrNone_FromString(rec->header_checksum_type)); PyDict_SetItemStringAndDecref(dict, "timestamp", PyLong_FromLongLong((PY_LONG_LONG) rec->timestamp)); PyDict_SetItemStringAndDecref(dict, "size", PyLong_FromLongLong((PY_LONG_LONG) rec->size)); PyDict_SetItemStringAndDecref(dict, "size_open", PyLong_FromLongLong((PY_LONG_LONG) rec->size_open)); PyDict_SetItemStringAndDecref(dict, "db_version", PyLong_FromLong((long) rec->db_version)); return dict; } PyObject * PyObject_FromYumRepoMd(LrYumRepoMd *repomd) { PyObject *dict, *list; if (!repomd) Py_RETURN_NONE; if ((dict = PyDict_New()) == NULL) return NULL; PyDict_SetItemStringAndDecref(dict, "revision", PyStringOrNone_FromString(repomd->revision)); list = PyList_New(0); for (GSList *elem = repomd->repo_tags; elem; elem = g_slist_next(elem)) { char *tag = elem->data; if (tag) PyList_Append(list, PyStringOrNone_FromString(tag)); } PyDict_SetItemStringAndDecref(dict, "repo_tags", list); list = PyList_New(0); for (GSList *elem = repomd->distro_tags; elem; elem = g_slist_next(elem)) { LrYumDistroTag *distrotag = elem->data; if (!elem->data) continue; char *cpeid = distrotag->cpeid; char *value = distrotag->tag; if (value) { PyList_Append(list, Py_BuildValue("(NN)", PyStringOrNone_FromString(cpeid), PyStringOrNone_FromString(value))); } } PyDict_SetItemStringAndDecref(dict, "distro_tags", list); list = PyList_New(0); for (GSList *elem = repomd->content_tags; elem; elem = g_slist_next(elem)) { char *tag = elem->data; if (tag) PyList_Append(list, PyStringOrNone_FromString(tag)); } PyDict_SetItemStringAndDecref(dict, "content_tags", list); for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) { LrYumRepoMdRecord *record = elem->data; if (!record) continue; PyDict_SetItemStringAndDecref(dict, record->type, PyObject_FromRepoMdRecord(record)); } return dict; } PyObject * PyObject_FromYumRepoMd_v2(LrYumRepoMd *repomd) { PyObject *dict, *list, *records; if (!repomd) Py_RETURN_NONE; if ((dict = PyDict_New()) == NULL) return NULL; PyDict_SetItemStringAndDecref(dict, "revision", PyStringOrNone_FromString(repomd->revision)); list = PyList_New(0); for (GSList *elem = repomd->repo_tags; elem; elem = g_slist_next(elem)) { char *tag = elem->data; if (tag) PyList_Append(list, PyStringOrNone_FromString(tag)); } PyDict_SetItemStringAndDecref(dict, "repo_tags", list); list = PyList_New(0); for (GSList *elem = repomd->distro_tags; elem; elem = g_slist_next(elem)) { LrYumDistroTag *distrotag = elem->data; if (!elem->data) continue; char *cpeid = distrotag->cpeid; char *value = distrotag->tag; if (value) { PyList_Append(list, Py_BuildValue("(NN)", PyStringOrNone_FromString(cpeid), PyStringOrNone_FromString(value))); } } PyDict_SetItemStringAndDecref(dict, "distro_tags", list); list = PyList_New(0); for (GSList *elem = repomd->content_tags; elem; elem = g_slist_next(elem)) { char *tag = elem->data; if (tag) PyList_Append(list, PyStringOrNone_FromString(tag)); } PyDict_SetItemStringAndDecref(dict, "content_tags", list); records = PyDict_New(); for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) { LrYumRepoMdRecord *record = elem->data; if (!record) continue; PyDict_SetItemStringAndDecref(records, record->type, PyObject_FromRepoMdRecord(record)); } PyDict_SetItemStringAndDecref(dict, "records", records); return dict; } PyObject * PyObject_FromMetalink(LrMetalink *metalink) { PyObject *dict, *sub_list; if (!metalink) Py_RETURN_NONE; if ((dict = PyDict_New()) == NULL) return NULL; PyDict_SetItemStringAndDecref(dict, "filename", PyStringOrNone_FromString(metalink->filename)); PyDict_SetItemStringAndDecref(dict, "timestamp", PyLong_FromLongLong((PY_LONG_LONG)metalink->timestamp)); PyDict_SetItemStringAndDecref(dict, "size", PyLong_FromLongLong((PY_LONG_LONG)metalink->size)); // Hashes if ((sub_list = PyList_New(0)) == NULL) { PyDict_Clear(dict); return NULL; } PyDict_SetItemStringAndDecref(dict, "hashes", sub_list); for (GSList *elem = metalink->hashes; elem; elem = g_slist_next(elem)) { LrMetalinkHash *metalinkhash = elem->data; PyObject *tuple; if ((tuple = PyTuple_New(2)) == NULL) { PyDict_Clear(dict); return NULL; } PyTuple_SetItem(tuple, 0, PyStringOrNone_FromString(metalinkhash->type)); PyTuple_SetItem(tuple, 1, PyStringOrNone_FromString(metalinkhash->value)); PyList_Append(sub_list, tuple); } // Urls if ((sub_list = PyList_New(0)) == NULL) { PyDict_Clear(dict); return NULL; } PyDict_SetItemStringAndDecref(dict, "urls", sub_list); for (GSList *elem = metalink->urls; elem; elem = g_slist_next(elem)) { LrMetalinkUrl *metalinkurl = elem->data; PyObject *udict; if ((udict = PyDict_New()) == NULL) { PyDict_Clear(dict); return NULL; } PyDict_SetItemStringAndDecref(udict, "protocol", PyStringOrNone_FromString(metalinkurl->protocol)); PyDict_SetItemStringAndDecref(udict, "type", PyStringOrNone_FromString(metalinkurl->type)); PyDict_SetItemStringAndDecref(udict, "location", PyStringOrNone_FromString(metalinkurl->location)); PyDict_SetItemStringAndDecref(udict, "preference", PyLong_FromLong((long) metalinkurl->preference)); PyDict_SetItemStringAndDecref(udict, "url", PyStringOrNone_FromString(metalinkurl->url)); PyList_Append(sub_list, udict); } // Alternates if (metalink->alternates) { if ((sub_list = PyList_New(0)) == NULL) { PyDict_Clear(dict); return NULL; } PyDict_SetItemStringAndDecref(dict, "alternates", sub_list); for (GSList *elem = metalink->alternates; elem; elem = g_slist_next(elem)) { LrMetalinkAlternate *ma = elem->data; PyObject *udict; if ((udict = PyDict_New()) == NULL) { PyDict_Clear(dict); return NULL; } PyDict_SetItemStringAndDecref(udict, "timestamp", PyLong_FromLongLong((PY_LONG_LONG)ma->timestamp)); PyDict_SetItemStringAndDecref(udict, "size", PyLong_FromLongLong((PY_LONG_LONG)ma->size)); PyObject *usub_list; if ((usub_list = PyList_New(0)) == NULL) { PyDict_Clear(dict); return NULL; } PyDict_SetItemStringAndDecref(udict, "hashes", usub_list); for (GSList *subelem = ma->hashes; subelem; subelem = g_slist_next(subelem)) { LrMetalinkHash *metalinkhash = subelem->data; PyObject *tuple; if ((tuple = PyTuple_New(2)) == NULL) { PyDict_Clear(dict); return NULL; } PyTuple_SetItem(tuple, 0, PyStringOrNone_FromString(metalinkhash->type)); PyTuple_SetItem(tuple, 1, PyStringOrNone_FromString(metalinkhash->value)); PyList_Append(usub_list, tuple); } PyList_Append(sub_list, udict); } } return dict; } librepo-1.12.1/librepo/python/typeconversion.h000066400000000000000000000027101372027637700214630ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_TYPECONVERSION_PY_H__ #define __LR_TYPECONVERSION_PY_H__ #include "librepo/repomd.h" #include "librepo/yum.h" #include "librepo/metalink.h" PyObject *PyStringOrNone_FromString(const char *str); PyObject *PyObject_FromYumRepo(LrYumRepo *repo); PyObject *PyObject_FromYumRepo_v2(LrYumRepo *repo); PyObject *PyObject_FromYumRepoMd(LrYumRepoMd *repomd); PyObject *PyObject_FromYumRepoMd_v2(LrYumRepoMd *repomd); PyObject *PyObject_FromMetalink(LrMetalink *metalink); char *PyAnyStr_AsString(PyObject *str, PyObject **tmp_py_str); #endif librepo-1.12.1/librepo/python/yum-py.c000066400000000000000000000027601372027637700176340ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #undef NDEBUG #include #include "librepo/librepo.h" #include "typeconversion.h" #include "exception-py.h" #include "result-py.h" PyObject * py_yum_repomd_get_age(G_GNUC_UNUSED PyObject *self, PyObject *args) { LrResult *res; PyObject *py_res; double age; if (!PyArg_ParseTuple(args, "O!:py_yum_repomd_get_age", &Result_Type, &py_res)) return NULL; res = Result_FromPyObject(py_res); if (!res) return NULL; age = lr_yum_repomd_get_age(res); return PyFloat_FromDouble(age); } librepo-1.12.1/librepo/python/yum-py.h000066400000000000000000000020731372027637700176360ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_YUM_PY_H__ #define __LR_YUM_PY_H__ #include "librepo/librepo.h" PyObject *py_yum_repomd_get_age(PyObject *self, PyObject *args); #endif librepo-1.12.1/librepo/rcodes.c000066400000000000000000000124541372027637700163330ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "rcodes.h" const char * lr_strerror(int rc) { switch (rc) { case LRE_OK: return "No error"; case LRE_BADFUNCARG: return "Bad function argument(s)"; case LRE_BADOPTARG: return "Bad argument of a handle option"; case LRE_UNKNOWNOPT: return "Unknown option"; case LRE_CURLSETOPT: return "curl_*_setopt() failed (too old curl version?)"; case LRE_ALREADYUSEDRESULT: return "LrResult object is not clean"; case LRE_INCOMPLETERESULT: return "LrResult object doesn't contain all needed info"; case LRE_CURLDUP: return "Cannot duplicate curl handle"; case LRE_CURL: return "An Curl handle error"; case LRE_CURLM: return "An Curl multi handle error"; case LRE_BADSTATUS: return "Error HTTP/FTP status code"; case LRE_TEMPORARYERR: return "Temporary error (operation timeout, ...), next try could work"; case LRE_NOTLOCAL: return "Repository URL is not a local path"; case LRE_CANNOTCREATEDIR: return "Cannot create output directory"; case LRE_IO: return "Input/Output error"; case LRE_MLBAD: return "Bad mirrorlist or metalink file"; case LRE_MLXML: return "Metalink XML parse error"; case LRE_BADCHECKSUM: return "Bad checksum"; case LRE_REPOMDXML: return "repomd.xml XML parse error"; case LRE_NOURL: return "Usable URL not found"; case LRE_CANNOTCREATETMP: return "Cannot create temp directory"; case LRE_UNKNOWNCHECKSUM: return "Unknown type of checksum is needed to verify one or more file(s)"; case LRE_BADURL: return "Bad URL specified"; case LRE_GPGNOTSUPPORTED: return "GPGME protocol is not supported"; case LRE_GPGERROR: return "Error while GPG check"; case LRE_BADGPG: return "Bad GPG signature"; case LRE_INCOMPLETEREPO: return "Repository metadata are not complete"; case LRE_INTERRUPTED: return "Interrupted by SIGINT"; case LRE_SIGACTION: return "Cannot set own signal handler - sigaction system call failed"; case LRE_ALREADYDOWNLOADED: return "File already exists and checksum is ok"; case LRE_UNFINISHED: return "Download wasn't or cannot be finished"; case LRE_SELECT: return "select() call failed"; case LRE_OPENSSL: return "OpenSSL library related error"; case LRE_MEMORY: return "Cannot allocate more memory"; case LRE_XMLPARSER: return "XML parser error"; case LRE_CBINTERRUPTED: return "Interrupted by user cb"; case LRE_REPOMD: return "Error in repomd.xml"; case LRE_VALUE: return "Bad value (no value, unknown unit, etc.)"; case LRE_NOTSET: return "Requested option/value is not set"; case LRE_FILE: return "File operation error"; case LRE_KEYFILE: return "Key file parsing error"; } return "Unknown error"; } GQuark lr_checksum_error_quark(void) { return g_quark_from_static_string("lr_checksum_error"); } GQuark lr_downloader_error_quark(void) { return g_quark_from_static_string("lr_downloader_error"); } GQuark lr_fastestmirror_error_quark(void) { return g_quark_from_static_string("lr_fastestmirror_error"); } GQuark lr_gpg_error_quark(void) { return g_quark_from_static_string("lr_gpg_error"); } GQuark lr_handle_error_quark(void) { return g_quark_from_static_string("lr_handle_error"); } GQuark lr_metalink_error_quark(void) { return g_quark_from_static_string("lr_metalink_error"); } GQuark lr_mirrorlist_error_quark(void) { return g_quark_from_static_string("lr_mirrorlist_error"); } GQuark lr_package_downloader_error_quark(void) { return g_quark_from_static_string("lr_package_downloader_error"); } GQuark lr_xml_parser_error_quark(void) { return g_quark_from_static_string("lr_xml_parser_error"); } GQuark lr_repoconf_error_quark(void) { return g_quark_from_static_string("lr_repoconf_error"); } GQuark lr_repomd_error_quark(void) { return g_quark_from_static_string("lr_repomd_error"); } GQuark lr_repoutil_yum_error_quark(void) { return g_quark_from_static_string("lr_repoutil_yum_error"); } GQuark lr_result_error_quark(void) { return g_quark_from_static_string("lr_result_error"); } GQuark lr_yum_error_quark(void) { return g_quark_from_static_string("lr_yum_error"); } librepo-1.12.1/librepo/rcodes.h000066400000000000000000000144521372027637700163400ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_RETURN_CODES_H__ #define __LR_RETURN_CODES_H__ #include G_BEGIN_DECLS /** \defgroup rcodes Error/Return codes * \addtogroup rcodes * @{ */ /** Librepo return/error codes */ typedef enum { LRE_OK, /*!< (0) everything is ok */ LRE_BADFUNCARG, /*!< (1) bad function argument */ LRE_BADOPTARG, /*!< (2) bad argument of the option */ LRE_UNKNOWNOPT, /*!< (3) library doesn't know the option */ LRE_CURLSETOPT, /*!< (4) cURL doesn't know the option. Too old curl version? */ LRE_ALREADYUSEDRESULT, /*!< (5) LrResult object is not clean */ LRE_INCOMPLETERESULT, /*!< (6) LrResult doesn't contain all what is needed */ LRE_CURLDUP, /*!< (7) cannot duplicate curl handle */ LRE_CURL, /*!< (8) cURL error */ LRE_CURLM, /*!< (9) cULR multi handle error */ LRE_BADSTATUS, /*!< (10) HTTP or FTP returned status code which do not represent success (file doesn't exists, etc.) */ LRE_TEMPORARYERR, /*!< (11) some error that should be temporary and next try could work (HTTP status codes 500, 502-504, operation timeout, ...) */ LRE_NOTLOCAL, /*!< (12) URL is not a local address */ LRE_CANNOTCREATEDIR, /*!< (13) cannot create a directory in output dir (already exists?) */ LRE_IO, /*!< (14) input output error */ LRE_MLBAD, /*!< (15) bad mirrorlist/metalink file (metalink doesn't contain needed file, mirrorlist doesn't contain urls, ..) */ LRE_MLXML, /*!< (16) metalink XML parse error */ LRE_BADCHECKSUM, /*!< (17) bad checksum */ LRE_REPOMDXML, /*!< (18) repomd XML parse error */ LRE_NOURL, /*!< (19) usable URL not found */ LRE_CANNOTCREATETMP, /*!< (20) cannot create tmp directory */ LRE_UNKNOWNCHECKSUM, /*!< (21) unknown type of checksum is needed for verification */ LRE_BADURL, /*!< (22) bad URL specified */ LRE_GPGNOTSUPPORTED, /*!< (23) OpenPGP protocol is not supported */ LRE_GPGERROR, /*!< (24) GPGME related error */ LRE_BADGPG, /*!< (25) Bad GPG signature */ LRE_INCOMPLETEREPO, /*!< (26) Repository metadata are not complete */ LRE_INTERRUPTED, /*!< (27) Download was interrupted by signal. Only if LRO_INTERRUPTIBLE option is enabled. */ LRE_SIGACTION, /*!< (28) sigaction error */ LRE_ALREADYDOWNLOADED, /*!< (29) File already exists and checksum is ok.*/ LRE_UNFINISHED, /*!< (30) The download wasn't or cannot be finished. */ LRE_SELECT, /*!< (31) select() call failed. */ LRE_OPENSSL, /*!< (32) OpenSSL library related error. */ LRE_MEMORY, /*!< (33) Cannot allocate more memory */ LRE_XMLPARSER, /*!< (34) XML parser error */ LRE_CBINTERRUPTED, /*!< (35) Interrupted by user cb */ LRE_REPOMD, /*!< (36) Error with repomd (bad content, missing expected values, ...) */ LRE_VALUE, /*!< (37) Bad value (e.g. we are expecting bandwidth defined like '1024', '1k', etc., but we got something like 'asdf', '1024S', etc.) */ LRE_NOTSET, /*!< (38) Requested option/value is not set. Used for example in lr_yum_repoconf_getinfo() */ LRE_FILE, /*!< (39) File operation error (operation not permitted, filename too long, no memory available, bad file descriptor, ...) */ LRE_KEYFILE, /*!< (40) Key file error (unknown encoding, ill-formed, file not found, key/group not found, ...) */ LRE_ZCK, /*!< (41) Zchunk error (error reading zchunk file, ...) */ LRE_UNKNOWNERROR, /*!< (xx) unknown error - sentinel of error codes enum */ } LrRc; /*!< Return codes */ /** Converts LrRc return code to error string. * @param rc LrRc code * @return Error string */ const char *lr_strerror(int rc); /** Error domains for GError */ #define LR_CHECKSUM_ERROR lr_checksum_error_quark() #define LR_DOWNLOADER_ERROR lr_downloader_error_quark() #define LR_FASTESTMIRROR_ERROR lr_fastestmirror_error_quark() #define LR_GPG_ERROR lr_gpg_error_quark() #define LR_HANDLE_ERROR lr_handle_error_quark() #define LR_METALINK_ERROR lr_metalink_error_quark() #define LR_MIRRORLIST_ERROR lr_mirrorlist_error_quark() #define LR_PACKAGE_DOWNLOADER_ERROR lr_package_downloader_error_quark() #define LR_REPOCONF_ERROR lr_repoconf_error_quark() #define LR_REPOMD_ERROR lr_repomd_error_quark() #define LR_REPOUTIL_YUM_ERROR lr_repoutil_yum_error_quark() #define LR_RESULT_ERROR lr_result_error_quark() #define LR_XML_PARSER_ERROR lr_xml_parser_error_quark() #define LR_YUM_ERROR lr_yum_error_quark() GQuark lr_checksum_error_quark(void); GQuark lr_downloader_error_quark(void); GQuark lr_fastestmirror_error_quark(void); GQuark lr_gpg_error_quark(void); GQuark lr_handle_error_quark(void); GQuark lr_metalink_error_quark(void); GQuark lr_mirrorlist_error_quark(void); GQuark lr_package_downloader_error_quark(void); GQuark lr_repoconf_error_quark(void); GQuark lr_repomd_error_quark(void); GQuark lr_repoutil_yum_error_quark(void); GQuark lr_result_error_quark(void); GQuark lr_xml_parser_error_quark(void); GQuark lr_yum_error_quark(void); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/repoconf.c000066400000000000000000001041531372027637700166650ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2014 Tomas Mlcoch * Copyright (C) 2014 Richard Hughes * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "librepo.h" #include "repoconf.h" #include "repoconf_internal.h" #include "cleanup.h" static LrYumRepoFile * lr_yum_repofile_init(const gchar *path, GKeyFile *keyfile) { LrYumRepoFile *repofile = lr_malloc0(sizeof(*repofile)); repofile->path = g_strdup(path); repofile->keyfile = keyfile; return repofile; } static void lr_yum_repofile_free(LrYumRepoFile *repofile) { if (!repofile) return; g_free(repofile->path); g_key_file_unref(repofile->keyfile); g_free(repofile); } static GKeyFile * repofile_keyfile(LrYumRepoFile *repofile) { return repofile->keyfile; } static void repofile_set_modified(LrYumRepoFile *repofile, gboolean val) { repofile->modified = val; } static gboolean repofile_modified(LrYumRepoFile *repofile) { return repofile->modified; } static gboolean repofile_save_keyfile(LrYumRepoFile *repofile, GError **err) { assert(!err || *err == NULL); gboolean ret; GError *tmp_err = NULL; if (!repofile_modified(repofile)) // File hasn't been modified return TRUE; // Write the contents of the keyfile ret = lr_key_file_save_to_file(repofile->keyfile, repofile->path, &tmp_err); // Act upon the return status if (ret) repofile_set_modified(repofile, FALSE); else g_set_error(err, LR_REPOCONF_ERROR, LRE_KEYFILE, "Cannot save conf to %s: %s", repofile->path, tmp_err->message); return ret; } static LrYumRepoConf * lr_yum_repoconf_init(LrYumRepoFile *repofile, const gchar *id) { LrYumRepoConf *repoconf = lr_malloc0(sizeof(*repoconf)); repoconf->file = repofile; repoconf->id = g_strdup(id); return repoconf; } static void lr_yum_repoconf_free(LrYumRepoConf *repoconf) { if (!repoconf) return; g_free(repoconf->id); g_free(repoconf); } static LrYumRepoFile * repoconf_repofile(LrYumRepoConf *repoconf) { return repoconf->file; } static GKeyFile * repoconf_keyfile(LrYumRepoConf *repoconf) { return repofile_keyfile(repoconf_repofile(repoconf)); } static gchar * repoconf_id(LrYumRepoConf *repoconf) { return repoconf->id; } LrYumRepoConfs * lr_yum_repoconfs_init(void) { LrYumRepoConfs *repos = lr_malloc0(sizeof(*repos)); return repos; } void lr_yum_repoconfs_free(LrYumRepoConfs *repos) { if (!repos) return; g_slist_free_full(repos->repos, (GDestroyNotify) lr_yum_repoconf_free); g_slist_free_full(repos->files, (GDestroyNotify) lr_yum_repofile_free); g_free(repos); } GSList * lr_yum_repoconfs_get_list(LrYumRepoConfs *repos, G_GNUC_UNUSED GError **err) { return repos->repos; } /* This function is taken from libhif * Original author: Richard Hughes */ static GKeyFile * lr_load_multiline_key_file(const char *filename, GError **err) { GKeyFile *file = NULL; gboolean ret; gsize len; guint i; _cleanup_error_free_ GError *tmp_err = NULL; _cleanup_free_ gchar *data = NULL; _cleanup_string_free_ GString *string = NULL; _cleanup_strv_free_ gchar **lines = NULL; // load file if (!g_file_get_contents (filename, &data, &len, &tmp_err)) { g_set_error(err, LR_REPOCONF_ERROR, LRE_FILE, "Cannot load content of %s: %s", filename, tmp_err->message); return NULL; } // split into lines string = g_string_new (""); lines = g_strsplit (data, "\n", -1); for (i = 0; lines[i] != NULL; i++) { // convert tabs to spaces g_strdelimit (lines[i], "\t", ' '); // if a line starts with whitespace, then append it on // the previous line if (lines[i][0] == ' ' && string->len > 0) { // remove old newline from previous line g_string_set_size (string, string->len - 1); // whitespace strip this new line g_strchug (lines[i]); // only add a ';' if we have anything after the '=' if (string->str[string->len - 1] == '=') { g_string_append_printf (string, "%s\n", lines[i]); } else { g_string_append_printf (string, ";%s\n", lines[i]); } } else { g_string_append_printf (string, "%s\n", lines[i]); } } // remove final newline if (string->len > 0) g_string_set_size (string, string->len - 1); // load modified lines file = g_key_file_new (); ret = g_key_file_load_from_data (file, string->str, -1, G_KEY_FILE_KEEP_COMMENTS, &tmp_err); if (!ret) { g_key_file_free (file); g_set_error(err, LR_REPOCONF_ERROR, LRE_KEYFILE, "Cannot parse key file %s: %s", filename, tmp_err->message); return NULL; } return file; } static gboolean lr_key_file_get_boolean(GKeyFile *keyfile, const gchar *groupname, const gchar *key, gboolean default_value, GError **err) { _cleanup_free_ gchar *string = NULL; _cleanup_free_ gchar *string_lower = NULL; string = g_key_file_get_string(keyfile, groupname, key, err); if (!string) return default_value; string_lower = g_ascii_strdown (string, -1); if (!g_strcmp0(string_lower, "1") || !g_strcmp0(string_lower, "yes") || !g_strcmp0(string_lower, "true")) return TRUE; return FALSE; } static long lr_key_file_get_boolean_long(GKeyFile *keyfile, const gchar *groupname, const gchar *key, gboolean default_value, GError **err) { if (lr_key_file_get_boolean(keyfile, groupname, key, default_value, err)) return 1; return 0; } static gchar ** lr_key_file_get_string_list(GKeyFile *keyfile, const gchar *groupname, const gchar *key, GError **err) { gchar **list = NULL; _cleanup_free_ gchar *string = NULL; string = g_key_file_get_string(keyfile, groupname, key, err); if (!string) return list; list = g_strsplit_set(string, " ,;", 0); gint i=0; for (; list && list[i]; i++) g_strstrip(list[i]); if (((i-1) > 0) && !g_strcmp0(list[i-1], "")) { // If the last element of the list is an empty string remove it // If could be cause when a list is defined like: option = val1; val2; g_free(list[i-1]); list[i-1] = NULL; } return list; } static LrIpResolveType lr_key_file_get_ip_resolve(GKeyFile *keyfile, const gchar *groupname, const gchar *key, LrIpResolveType default_value, GError **err) { _cleanup_free_ gchar *string = NULL; _cleanup_free_ gchar *string_lower = NULL; string = g_key_file_get_string(keyfile, groupname, key, err); if (!string) return default_value; string_lower = g_ascii_strdown(string, -1); if (!g_strcmp0(string_lower, "ipv4")) return LR_IPRESOLVE_V4; else if (!g_strcmp0(string_lower, "ipv6")) return LR_IPRESOLVE_V6; else if (!g_strcmp0(string_lower, "whatever")) return LR_IPRESOLVE_WHATEVER; else g_set_error(err, LR_REPOCONF_ERROR, LRE_VALUE, "Unknown ip_resolve value '%s'", string); return default_value; } static void lr_key_file_set_ip_resolve(GKeyFile *keyfile, const gchar *groupname, const gchar *key, LrIpResolveType resolve_type) { if (resolve_type == LR_IPRESOLVE_WHATEVER) g_key_file_set_string(keyfile, groupname, key, "whatever"); else if (resolve_type == LR_IPRESOLVE_V4) g_key_file_set_string(keyfile, groupname, key, "ipv4"); else if (resolve_type == LR_IPRESOLVE_V6) g_key_file_set_string(keyfile, groupname, key, "ipv6"); } static gboolean lr_convert_interval_to_seconds(const char *str, gint64 *out, GError **err) { gdouble value = 0.0; gdouble mult = 1.0; gchar *endptr = NULL; *out = G_GINT64_CONSTANT(0); // Initial sanity checking if (!str) { g_set_error(err, LR_REPOCONF_ERROR, LRE_BADFUNCARG, "No time interval value specified"); return FALSE; } // Initial conversion value = g_ascii_strtod(str, &endptr); if (value == HUGE_VAL && errno == ERANGE) { g_set_error(err, LR_REPOCONF_ERROR, LRE_VALUE, "Too big time interval value '%s'", str); return FALSE; } // String doesn't start with numbers if (endptr == str) { g_set_error(err, LR_REPOCONF_ERROR, LRE_VALUE, "Couldn't convert '%s' to seconds", str); return FALSE; } // Process multiplier (if supplied) if (endptr && *endptr) { if (strlen(endptr) != 1) { g_set_error(err, LR_REPOCONF_ERROR, LRE_VALUE, "Unknown time interval unit '%s'", endptr); return FALSE; } gchar unit = g_ascii_tolower(*endptr); if (unit == 's') { mult = 1.0; } else if (unit == 'm') { mult = 60.0; } else if (unit == 'h') { mult = 60.0 * 60.0; } else if (unit == 'd') { mult = 60.0 * 60.0 * 24.0; } else { g_set_error(err, LR_REPOCONF_ERROR, LRE_VALUE, "Unknown time interval unit '%s'", endptr); return FALSE; } } // Convert result to seconds value = value * mult; // Return result as integer *out = (gint64) value; return TRUE; } static gint64 lr_key_file_get_metadata_expire(GKeyFile *keyfile, const gchar *groupname, const gchar *key, gint64 default_value, GError **err) { gint64 res = G_GINT64_CONSTANT(-1); _cleanup_free_ gchar *string = NULL; string = g_key_file_get_string(keyfile, groupname, key, err); if (!string) return default_value; if (!lr_convert_interval_to_seconds(string, &(res), err)) return G_GINT64_CONSTANT(-1); return res; } static gboolean lr_convert_bandwidth_to_bytes(const char *str, guint64 *out, GError **err) { gdouble dbytes = 0.0; gdouble mult = 1.0; gchar *endptr = NULL; *out = G_GUINT64_CONSTANT(0); // Initial sanity checking if (!str) { g_set_error(err, LR_REPOCONF_ERROR, LRE_BADFUNCARG, "No bandwidth value specified"); return FALSE; } // Initial conversion dbytes = g_ascii_strtod(str, &endptr); if (dbytes == HUGE_VAL && errno == ERANGE) { g_set_error(err, LR_REPOCONF_ERROR, LRE_VALUE, "Too big bandwidth value '%s'", str); return FALSE; } // String doesn't start with numbers if (endptr == str) { g_set_error(err, LR_REPOCONF_ERROR, LRE_VALUE, "Couldn't convert '%s' to number", str); return FALSE; } // Process multiplier (if supplied) if (endptr && *endptr) { if (strlen(endptr) != 1) { g_set_error(err, LR_REPOCONF_ERROR, LRE_VALUE, "Unknown unit '%s'", endptr); return FALSE; } gchar unit = g_ascii_tolower(*endptr); if (unit == 'k') { mult = 1024.0; } else if (unit == 'm') { mult = 1024.0 * 1024.0; } else if (unit == 'g') { mult = 1024.0 * 1024.0 * 1024.0; } else { g_set_error(err, LR_REPOCONF_ERROR, LRE_VALUE, "Unknown unit '%s'", endptr); return FALSE; } } // Convert result to bytes dbytes = dbytes * mult; if (dbytes < 0.0) { g_set_error(err, LR_REPOCONF_ERROR, LRE_VALUE, "Bytes value may not be negative '%s'", str); return FALSE; } // Return result as integer *out = (guint64) dbytes; return TRUE; } static guint64 lr_key_file_get_bandwidth(GKeyFile *keyfile, const gchar *groupname, const gchar *key, guint64 default_value, GError **err) { guint64 res = G_GUINT64_CONSTANT(0); _cleanup_free_ gchar *string = NULL; string = g_key_file_get_string(keyfile, groupname, key, err); if (!string) return default_value; if (!lr_convert_bandwidth_to_bytes(string, &(res), err)) return G_GUINT64_CONSTANT(0); return res; } static void lr_key_file_set_string(GKeyFile *keyfile, const gchar *groupname, const gchar *key, const gchar *str) { if (!str) { g_key_file_remove_key(keyfile, groupname, key, NULL); return; } g_key_file_set_string(keyfile, groupname, key, str); } static void lr_key_file_set_string_list(GKeyFile *keyfile, const gchar *groupname, const gchar *key, const gchar **list) { gsize len = 0; if (list) len = g_strv_length((gchar **) list); if (len == 0) { g_key_file_remove_key(keyfile, groupname, key, NULL); return; } g_key_file_set_string_list(keyfile, groupname, key, (const gchar * const*) list, len); } gboolean lr_yum_repoconfs_add_empty_conf(LrYumRepoConfs *repos, const char *filename, const char **ids, GError **err) { GKeyFile *keyfile; LrYumRepoFile *repofile; assert(!err || *err == NULL); if (!repos) { g_set_error(err, LR_REPOCONF_ERROR, LRE_BADFUNCARG, "No yumrepoconfs arg specified"); return FALSE; } if (!filename) { g_set_error(err, LR_REPOCONF_ERROR, LRE_BADFUNCARG, "No filename specified"); return FALSE; } keyfile = g_key_file_new(); repofile = lr_yum_repofile_init(filename, keyfile); repos->files = g_slist_append(repos->files, repofile); for (guint i = 0; ids[i]; i++) { LrYumRepoConf *repoconf = NULL; repoconf = lr_yum_repoconf_init(repofile, ids[i]); if (!repoconf) return FALSE; repos->repos = g_slist_append(repos->repos, repoconf); } return TRUE; } gboolean lr_yum_repoconfs_parse(LrYumRepoConfs *repos, const char *filename, GError **err) { _cleanup_strv_free_ gchar **groups = NULL; LrYumRepoFile *repofile = NULL; GKeyFile *keyfile = NULL; // Load key file content keyfile = lr_load_multiline_key_file(filename, err); if (!keyfile) return FALSE; // Create LrYumRepoFile object repofile = lr_yum_repofile_init(filename, keyfile); repos->files = g_slist_append(repos->files, repofile); // Create LrYumRepoConf objects groups = g_key_file_get_groups (keyfile, NULL); for (guint i = 0; groups[i]; i++) { LrYumRepoConf *repoconf = NULL; repoconf = lr_yum_repoconf_init(repofile, groups[i]); if (!repoconf) return FALSE; repos->repos = g_slist_append(repos->repos, repoconf); } return TRUE; } gboolean lr_yum_repoconfs_load_dir(LrYumRepoConfs *repos, const char *path, GError **err) { const gchar *file; _cleanup_error_free_ GError *tmp_err = NULL; _cleanup_dir_close_ GDir *dir = NULL; // Open dir dir = g_dir_open(path, 0, &tmp_err); if (!dir) { g_set_error(err, LR_REPOCONF_ERROR, LRE_KEYFILE, "Cannot open dir %s: %s", path, tmp_err->message); return FALSE; } // Find all the .repo files while ((file = g_dir_read_name(dir))) { _cleanup_free_ gchar *path_tmp = NULL; if (!g_str_has_suffix(file, ".repo")) continue; path_tmp = g_build_filename(path, file, NULL); if (!lr_yum_repoconfs_parse(repos, path_tmp, err)) return FALSE; } return TRUE; } gboolean lr_yum_repoconfs_save(LrYumRepoConfs *confs, GError **err) { assert(!err || *err == NULL); if (!confs) { g_set_error(err, LR_REPOCONF_ERROR, LRE_BADFUNCARG, "No yumrepoconfs arg specified"); return FALSE; } for (GSList *elem = confs->repos; elem; elem = g_slist_next(elem)) { LrYumRepoConf *repoconf = elem->data; if (!lr_yum_repoconf_save(repoconf, err)) return FALSE; } return TRUE; } gboolean lr_yum_repoconf_save(LrYumRepoConf *repoconf, GError **err) { assert(!err || *err == NULL); if (!repoconf) { g_set_error(err, LR_REPOCONF_ERROR, LRE_BADFUNCARG, "No config specified"); return FALSE; } LrYumRepoFile *repofile = repoconf_repofile(repoconf); return repofile_save_keyfile(repofile, err); } gboolean lr_yum_repoconf_getinfo(LrYumRepoConf *repoconf, GError **err, LrYumRepoConfOption option, ...) { GError *tmp_err = NULL; va_list arg; char **str; char ***strv; long *lnum; assert(!err || *err == NULL); if (!repoconf) { g_set_error(err, LR_REPOCONF_ERROR, LRE_BADFUNCARG, "No config specified"); return FALSE; } // Shortcuts GKeyFile *keyfile = repoconf_keyfile(repoconf); gchar *id = repoconf_id(repoconf); // Basic sanity checks if (!keyfile) { g_set_error(err, LR_REPOCONF_ERROR, LRE_BADFUNCARG, "No keyfile available in yumrepoconf"); return FALSE; } va_start(arg, option); switch (option) { case LR_YRC_ID: /*!< (char *) ID (short name) of the repo */ str = va_arg(arg, char **); *str = g_strdup(id); break; case LR_YRC_NAME: /*!< (char *) Pretty name of the repo */ str = va_arg(arg, char **); *str = g_key_file_get_string(keyfile, id, "name", &tmp_err); break; case LR_YRC_ENABLED: /*!< (long 1 or 0) Is repo enabled? */ lnum = va_arg(arg, long *); *lnum = lr_key_file_get_boolean_long(keyfile, id, "enabled", TRUE, &tmp_err); break; case LR_YRC_BASEURL: /*!< (char **) List of base URLs */ strv = va_arg(arg, char ***); *strv = lr_key_file_get_string_list(keyfile, id, "baseurl", &tmp_err); break; case LR_YRC_MIRRORLIST: /*!< (char *) Mirrorlist URL */ str = va_arg(arg, char **); *str = g_key_file_get_string(keyfile, id, "mirrorlist", &tmp_err); break; case LR_YRC_METALINK: /*!< (char *) Metalink URL */ str = va_arg(arg, char **); *str = g_key_file_get_string(keyfile, id, "metalink", &tmp_err); break; case LR_YRC_MEDIAID: /*!< (char *) Media ID */ str = va_arg(arg, char **); *str = g_key_file_get_string(keyfile, id, "mediaid", &tmp_err); break; case LR_YRC_GPGKEY: /*!< (char **) URL of GPG key */ strv = va_arg(arg, char ***); *strv = lr_key_file_get_string_list(keyfile, id, "gpgkey", &tmp_err); break; case LR_YRC_GPGCAKEY: /*!< (char **) GPG CA key */ strv = va_arg(arg, char ***); *strv = lr_key_file_get_string_list(keyfile, id, "gpgcakey", &tmp_err); break; case LR_YRC_EXCLUDE: /*!< (char **) List of excluded packages */ strv = va_arg(arg, char ***); *strv = lr_key_file_get_string_list(keyfile, id, "exclude", &tmp_err); break; case LR_YRC_INCLUDE: /*!< (char **) List of included packages */ strv = va_arg(arg, char ***); *strv = lr_key_file_get_string_list(keyfile, id, "include", &tmp_err); break; case LR_YRC_FASTESTMIRROR: /*!< (long 1 or 0) Fastest mirror determination */ lnum = va_arg(arg, long *); *lnum = lr_key_file_get_boolean_long(keyfile, id, "fastestmirror", TRUE, &tmp_err); break; case LR_YRC_PROXY: /*!< (char *) Proxy address */ str = va_arg(arg, char **); *str = g_key_file_get_string(keyfile, id, "proxy", &tmp_err); break; case LR_YRC_PROXY_USERNAME: /*!< (char *) Proxy username */ str = va_arg(arg, char **); *str = g_key_file_get_string(keyfile, id, "proxy_username", &tmp_err); break; case LR_YRC_PROXY_PASSWORD: /*!< (char *) Proxy password */ str = va_arg(arg, char **); *str = g_key_file_get_string(keyfile, id, "proxy_password", &tmp_err); break; case LR_YRC_USERNAME: /*!< (char *) Username */ str = va_arg(arg, char **); *str = g_key_file_get_string(keyfile, id, "username", &tmp_err); break; case LR_YRC_PASSWORD: /*!< (char *) Password */ str = va_arg(arg, char **); *str = g_key_file_get_string(keyfile, id, "password", &tmp_err); break; case LR_YRC_GPGCHECK: /*!< (long 1 or 0) GPG check for packages */ lnum = va_arg(arg, long *); *lnum = lr_key_file_get_boolean_long(keyfile, id, "gpgcheck", TRUE, &tmp_err); break; case LR_YRC_REPO_GPGCHECK: /*!< (long 1 or 0) GPG check for repodata */ lnum = va_arg(arg, long *); *lnum = lr_key_file_get_boolean_long(keyfile, id, "repo_gpgcheck", TRUE, &tmp_err); break; case LR_YRC_ENABLEGROUPS: /*!< (long 1 or 0) Use groups */ lnum = va_arg(arg, long *); *lnum = lr_key_file_get_boolean_long(keyfile, id, "enablegroups", TRUE, &tmp_err); break; case LR_YRC_BANDWIDTH: /*!< (guint64) Bandwidth - Number of bytes */ { guint64 *num = va_arg(arg, guint64 *); *num = lr_key_file_get_bandwidth(keyfile, id, "bandwidth", G_GUINT64_CONSTANT(0), &tmp_err); break; } case LR_YRC_THROTTLE: /*!< (char *) Throttle string */ str = va_arg(arg, char **); *str = g_key_file_get_string(keyfile, id, "throttle", &tmp_err); break; case LR_YRC_IP_RESOLVE: /*!< (LrIpResolveType) Ip resolve type */ { LrIpResolveType *val = va_arg(arg, LrIpResolveType *); *val = lr_key_file_get_ip_resolve(keyfile, id, "ip_resolve", LR_YUMREPOCONF_IP_RESOLVE_DEFAULT, &tmp_err); break; } case LR_YRC_METADATA_EXPIRE:/*!< (gint64) Interval in secs for metadata expiration */ { gint64 *num = va_arg(arg, gint64 *); *num = lr_key_file_get_metadata_expire(keyfile, id, "metadata_expire", LR_YUMREPOCONF_METADATA_EXPIRE_DEFAULT, &tmp_err); break; } case LR_YRC_COST: /*!< (gint) Repo cost */ { gint *num = va_arg(arg, gint *); *num = g_key_file_get_integer(keyfile, id, "cost", &tmp_err); break; } case LR_YRC_PRIORITY: /*!< (gint) Repo priority */ { gint *num = va_arg(arg, gint *); *num = g_key_file_get_integer(keyfile, id, "priority", &tmp_err); break; } case LR_YRC_SSLCACERT: /*!< (gchar *) SSL Certification authority cert */ str = va_arg(arg, char **); *str = g_key_file_get_string(keyfile, id, "sslcacert", &tmp_err); break; case LR_YRC_SSLVERIFY: /*!< (long 1 or 0) SSL verification */ lnum = va_arg(arg, long *); *lnum = lr_key_file_get_boolean_long(keyfile, id, "sslverify", TRUE, &tmp_err); break; case LR_YRC_SSLCLIENTCERT: /*!< (gchar *) SSL Client certificate */ str = va_arg(arg, char **); *str = g_key_file_get_string(keyfile, id, "sslclientcert", &tmp_err); break; case LR_YRC_SSLCLIENTKEY: /*!< (gchar *) SSL Client key */ str = va_arg(arg, char **); *str = g_key_file_get_string(keyfile, id, "sslclientkey", &tmp_err); break; case LR_YRC_DELTAREPOBASEURL:/*!< (char **) Deltarepo mirror URLs */ strv = va_arg(arg, char ***); *strv = lr_key_file_get_string_list(keyfile, id, "deltarepobaseurl", &tmp_err); break; case LR_YRC_FAILOVERMETHOD: /*!< (gchar *) Failover method */ str = va_arg(arg, char **); *str = g_key_file_get_string(keyfile, id, "failovermethod", &tmp_err); break; case LR_YRC_SKIP_IF_UNAVAILABLE:/*!< (long 1 or 0) Skip if unavailable */ lnum = va_arg(arg, long *); *lnum = lr_key_file_get_boolean_long(keyfile, id, "skip_if_unavailable", TRUE, &tmp_err); break; } va_end(arg); if (tmp_err) { if (tmp_err->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND || tmp_err->code == G_KEY_FILE_ERROR_GROUP_NOT_FOUND) g_set_error(err, LR_REPOCONF_ERROR, LRE_NOTSET, "Value of option %d is not set", option); else g_set_error(err, LR_REPOCONF_ERROR, LRE_KEYFILE, "Cannot get value of option %d: %s", option, tmp_err->message); g_error_free(tmp_err); return FALSE; } return TRUE; } gboolean lr_yum_repoconf_setopt(LrYumRepoConf *repoconf, GError **err, LrYumRepoConfOption option, ...) { GError *tmp_err = NULL; va_list arg; char *str; char **strv; long lnum; assert(!err || *err == NULL); if (!repoconf) { g_set_error(err, LR_REPOCONF_ERROR, LRE_BADFUNCARG, "No config specified"); return FALSE; } // Shortcuts LrYumRepoFile *repofile = repoconf_repofile(repoconf); GKeyFile *keyfile = repoconf_keyfile(repoconf); gchar *id = repoconf_id(repoconf); // Basic sanity checks if (!keyfile) { g_set_error(err, LR_REPOCONF_ERROR, LRE_BADFUNCARG, "No keyfile available in yumrepoconf"); return FALSE; } va_start(arg, option); switch (option) { case LR_YRC_ID: /*!< 0 (char *) ID (short name) of the repo */ g_set_error(&tmp_err, LR_REPOCONF_ERROR, LRE_BADOPTARG, "ID is read only option"); break; case LR_YRC_NAME: /*!< 1 (char *) Pretty name of the repo */ str = va_arg(arg, char *); lr_key_file_set_string(keyfile, id, "name", str); break; case LR_YRC_ENABLED: /*!< 2 (long 1 or 0) Is repo enabled? */ lnum = va_arg(arg, long); g_key_file_set_boolean(keyfile, id, "enabled", lnum ? TRUE : FALSE); break; case LR_YRC_BASEURL: /*!< 3 (char **) List of base URLs */ strv = va_arg(arg, char **); lr_key_file_set_string_list(keyfile, id, "baseurl", (const gchar **) strv); break; case LR_YRC_MIRRORLIST: /*!< 4 (char *) Mirrorlist URL */ str = va_arg(arg, char *); lr_key_file_set_string(keyfile, id, "mirrorlist", str); break; case LR_YRC_METALINK: /*!< 5 (char *) Metalink URL */ str = va_arg(arg, char *); lr_key_file_set_string(keyfile, id, "metalink", str); break; case LR_YRC_MEDIAID: /*!< 6 (char *) Media ID */ str = va_arg(arg, char *); lr_key_file_set_string(keyfile, id, "mediaid", str); break; case LR_YRC_GPGKEY: /*!< 7 (char **) URL of GPG key */ strv = va_arg(arg, char **); lr_key_file_set_string_list(keyfile, id, "gpgkey", (const gchar **) strv); break; case LR_YRC_GPGCAKEY: /*!< 8 (char **) GPG CA key */ strv = va_arg(arg, char **); lr_key_file_set_string_list(keyfile, id, "gpgcakey", (const gchar **) strv); break; case LR_YRC_EXCLUDE: /*!< 9 (char **) List of exluded packages */ strv = va_arg(arg, char **); lr_key_file_set_string_list(keyfile, id, "exclude", (const gchar **) strv); break; case LR_YRC_INCLUDE: /*!< 10 (char **) List of included packages */ strv = va_arg(arg, char **); lr_key_file_set_string_list(keyfile, id, "include", (const gchar **) strv); break; case LR_YRC_FASTESTMIRROR: /*!< 11 (long 1 or 0) Fastest mirror determination */ lnum = va_arg(arg, long); g_key_file_set_boolean(keyfile, id, "fastestmirror", lnum); break; case LR_YRC_PROXY: /*!< 12 (char *) Proxy address */ str = va_arg(arg, char *); lr_key_file_set_string(keyfile, id, "proxy", str); break; case LR_YRC_PROXY_USERNAME: /*!< 13 (char *) Proxy username */ str = va_arg(arg, char *); lr_key_file_set_string(keyfile, id, "proxy_username", str); break; case LR_YRC_PROXY_PASSWORD: /*!< 14 (char *) Proxy password */ str = va_arg(arg, char *); lr_key_file_set_string(keyfile, id, "proxy_password", str); break; case LR_YRC_USERNAME: /*!< 15 (char *) Username */ str = va_arg(arg, char *); lr_key_file_set_string(keyfile, id, "username", str); break; case LR_YRC_PASSWORD: /*!< 16 (char *) Password */ str = va_arg(arg, char *); lr_key_file_set_string(keyfile, id, "password", str); break; case LR_YRC_GPGCHECK: /*!< 17 (long 1 or 0) GPG check for packages */ lnum = va_arg(arg, long); g_key_file_set_boolean(keyfile, id, "gpgcheck", (gboolean) lnum); break; case LR_YRC_REPO_GPGCHECK: /*!< 18 (long 1 or 0) GPG check for repodata */ lnum = va_arg(arg, long); g_key_file_set_boolean(keyfile, id, "repo_gpgcheck", (gboolean) lnum); break; case LR_YRC_ENABLEGROUPS: /*!< 19 (long 1 or 0) Use groups */ lnum = va_arg(arg, long); g_key_file_set_boolean(keyfile, id, "enablegroups", (gboolean) lnum); break; case LR_YRC_BANDWIDTH: /*!< 20 (guint64) Bandwidth - Number of bytes */ { guint64 val = va_arg(arg, guint64); g_key_file_set_uint64(keyfile, id, "bandwidth", val); break; } case LR_YRC_THROTTLE: /*!< 21 (char *) Throttle string */ str = va_arg(arg, char *); lr_key_file_set_string(keyfile, id, "throttle", str); break; case LR_YRC_IP_RESOLVE: /*!< 22 (LrIpResolveType) Ip resolve type */ { LrIpResolveType val = va_arg(arg, LrIpResolveType); lr_key_file_set_ip_resolve(keyfile, id, "ip_resolve", val); break; } case LR_YRC_METADATA_EXPIRE: /*!< 23 (gint64) Interval in secs for metadata expiration */ { // TODO: Store value in appropriate units gint64 val = va_arg(arg, gint64); g_key_file_set_int64(keyfile, id, "metadata_expire", val); break; } case LR_YRC_COST: /*!< 24 (gint) Repo cost */ { gint val = va_arg(arg, gint); g_key_file_set_integer(keyfile, id, "cost", val); break; } case LR_YRC_PRIORITY: /*!< 25 (gint) Repo priority */ { guint val = va_arg(arg, gint); g_key_file_set_integer(keyfile, id, "priority", val); break; } case LR_YRC_SSLCACERT: /*!< 26 (gchar *) SSL Certification authority cert */ str = va_arg(arg, char *); lr_key_file_set_string(keyfile, id, "sslcacert", str); break; case LR_YRC_SSLVERIFY: /*!< 27 (long 1 or 0) SSL verification */ lnum = va_arg(arg, long); g_key_file_set_boolean(keyfile, id, "sslverify", (gboolean) lnum); break; case LR_YRC_SSLCLIENTCERT: /*!< 28 (gchar *) SSL Client certificate */ str = va_arg(arg, char *); lr_key_file_set_string(keyfile, id, "sslclientcert", str); break; case LR_YRC_SSLCLIENTKEY: /*!< 29 (gchar *) SSL Client key */ str = va_arg(arg, char *); lr_key_file_set_string(keyfile, id, "sslclientkey", str); break; case LR_YRC_DELTAREPOBASEURL:/*!< 30 (char **) Deltarepo mirror URLs */ strv = va_arg(arg, char **); lr_key_file_set_string_list(keyfile, id, "deltarepobaseurl", (const gchar **) strv); break; case LR_YRC_FAILOVERMETHOD: /*!< 31 (gchar *) Failover method */ str = va_arg(arg, char *); lr_key_file_set_string(keyfile, id, "failovermethod", str); break; case LR_YRC_SKIP_IF_UNAVAILABLE: /*!< 32 (long 1 or 0) Skip if unavailable */ lnum = va_arg(arg, long); g_key_file_set_boolean(keyfile, id, "skip_if_unavailable", (gboolean) lnum); break; } va_end(arg); if (tmp_err) { g_propagate_error(err, tmp_err); return FALSE; } repofile_set_modified(repofile, TRUE); return TRUE; } librepo-1.12.1/librepo/repoconf.h000066400000000000000000000171731372027637700166770ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2014 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_REPOCONF_H__ #define __LR_REPOCONF_H__ #include #include "types.h" G_BEGIN_DECLS /** \defgroup repoconf *.repo file * \addtogroup repoconf * @{ */ #define LR_YUMREPOCONF_BANDWIDTH_DEFAULT G_GUINT64_CONSTANT(0) #define LR_YUMREPOCONF_IP_RESOLVE_DEFAULT LR_IPRESOLVE_WHATEVER #define LR_YUMREPOCONF_METADATA_EXPIRE_DEFAULT G_GINT64_CONSTANT(172800) // 60*60*48 #define LR_YUMREPOCONF_COST_DEFAULT 1000 #define LR_YUMREPOCONF_PRIORITY_DEFAULT 99 /** Configuration for a single repo */ typedef struct _LrYumRepoConf LrYumRepoConf; /** List of yum repo configurations */ typedef struct _LrYumRepoConfs LrYumRepoConfs; typedef enum { LR_YRC_ID, /*!< 0 (char *) ID (short name) of the repo */ LR_YRC_NAME, /*!< 1 (char *) Pretty name of the repo */ LR_YRC_ENABLED, /*!< 2 (long 1 or 0) Is repo enabled? */ LR_YRC_BASEURL, /*!< 3 (char **) List of base URLs */ LR_YRC_MIRRORLIST, /*!< 4 (char *) Mirrorlist URL */ LR_YRC_METALINK, /*!< 5 (char *) Metalink URL */ LR_YRC_MEDIAID, /*!< 6 (char *) Media ID */ LR_YRC_GPGKEY, /*!< 7 (char **) URL of GPG key */ LR_YRC_GPGCAKEY, /*!< 8 (char **) GPG CA key */ LR_YRC_EXCLUDE, /*!< 9 (char **) List of excluded packages */ LR_YRC_INCLUDE, /*!< 10 (char **) List of included packages */ LR_YRC_FASTESTMIRROR, /*!< 11 (long 1 or 0) Fastest mirror determination */ LR_YRC_PROXY, /*!< 12 (char *) Proxy address */ LR_YRC_PROXY_USERNAME, /*!< 13 (char *) Proxy username */ LR_YRC_PROXY_PASSWORD, /*!< 14 (char *) Proxy password */ LR_YRC_USERNAME, /*!< 15 (char *) Username */ LR_YRC_PASSWORD, /*!< 16 (char *) Password */ LR_YRC_GPGCHECK, /*!< 17 (long 1 or 0) GPG check for packages */ LR_YRC_REPO_GPGCHECK, /*!< 18 (long 1 or 0) GPG check for repodata */ LR_YRC_ENABLEGROUPS, /*!< 19 (long 1 or 0) Use groups */ LR_YRC_BANDWIDTH, /*!< 20 (guint64) Bandwidth - Number of bytes */ LR_YRC_THROTTLE, /*!< 21 (char *) Throttle string */ LR_YRC_IP_RESOLVE, /*!< 22 (LrIpResolveType) Ip resolve type */ LR_YRC_METADATA_EXPIRE, /*!< 23 (gint64) Interval in secs for metadata expiration */ LR_YRC_COST, /*!< 24 (gint) Repo cost */ LR_YRC_PRIORITY, /*!< 25 (gint) Repo priority */ LR_YRC_SSLCACERT, /*!< 26 (gchar *) SSL Certification authority cert */ LR_YRC_SSLVERIFY, /*!< 27 (long 1 or 0) SSL verification */ LR_YRC_SSLCLIENTCERT, /*!< 28 (gchar *) SSL Client certificate */ LR_YRC_SSLCLIENTKEY, /*!< 29 (gchar *) SSL Client key */ LR_YRC_DELTAREPOBASEURL,/*!< 30 (char **) Deltarepo mirror URLs */ LR_YRC_FAILOVERMETHOD, /*!< 31 (char *) Failover method */ LR_YRC_SKIP_IF_UNAVAILABLE, /*!< 32 (long 1 or 0) Skip if unavailable */ } LrYumRepoConfOption; /** Return new empty LrYumRepoConfs * @return Newly allocated LrYumRepoConfs */ LrYumRepoConfs * lr_yum_repoconfs_init(void); /** Frees LrYumRepoConfs * @param confs NULL or LrYumRepoConfs */ void lr_yum_repoconfs_free(LrYumRepoConfs *confs); /** Get GSList of LrYumRepoConf from a LrYumRepoConfs. * @param confs LrYumRepoConfs (not NULL!) * @return Pointer to internal GSList in LrYumRepoConfs */ GSList * lr_yum_repoconfs_get_list(LrYumRepoConfs *confs, GError **err); /** Add an empty LrYumRepoConf. * A file specified by filename don't have to exists - it will be created. * @param confs LrYumRepoConfs * @param filename Path to *.repo file (the file doesn't have to exist) * @param ids IDs of repositories confs there (NULL-terminated list) * @param err GError ** * @return TRUE if everything is ok, FALSE if err is set. */ gboolean lr_yum_repoconfs_add_empty_conf(LrYumRepoConfs *confs, const char *filename, const char **ids, GError **err); /** Parse a *.repo file * @param confs LrYumRepoConfs * @param filename Path to *.repo file * @return TRUE if everything is ok, FALSE if err is set. */ gboolean lr_yum_repoconfs_parse(LrYumRepoConfs *confs, const char *filename, GError **err); /** Load a directory with *.repo files (e.g. /etc/yum.repos.d/) * @param confs LrYumRepoConfs * @param path Path to a directory with *.repo files * @return TRUE if everything is ok, FALSE if err is set. */ gboolean lr_yum_repoconfs_load_dir(LrYumRepoConfs *confs, const char *path, GError **err); /** Save all repoconfs loaded in LrYumRepoConfs * @param confs LrYumRepoConfs * @param err GError ** * @return TRUE if everything is ok, FALSE if err is set. */ gboolean lr_yum_repoconfs_save(LrYumRepoConfs *confs, GError **err); /** Save current repoconf (and also all repoconfs loaded from the same file). * @param repoconf LrYumRepoConf * @param err GError ** * @return TRUE if everything is ok, FALSE if err is set. */ gboolean lr_yum_repoconf_save(LrYumRepoConf *repoconf, GError **err); /** Get a value from repo config file *.repo * If specified option is not specified in the repo config file, * FALSE is returned and error with code LRE_VALUE is set. * Note: All returned values are malloced and must be freed by caller. * @param repoconf A repository configuration * @param err GError ** * @param option An option * @param ... Appropriate variable for the selected option. * @return TRUE if everything is ok, FALSE if err is set. * Note value of the target variable passed as vararg * can be changed and it's state is undefined when * a FALSE is returned! */ gboolean lr_yum_repoconf_getinfo(LrYumRepoConf *repoconf, GError **err, LrYumRepoConfOption option, ...); /** Set an option in the config file. * Note: This function copies all passed values and * caller don't have to keep them around. * @param repoconf A repo configuration * @param err GError ** * @param option An option * @param ... A value for an option as an appropriate variable type. * @return TRUE if everything is OK, FALSE if err is set. */ gboolean lr_yum_repoconf_setopt(LrYumRepoConf *repoconf, GError **err, LrYumRepoConfOption option, ...); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/repoconf_internal.h000066400000000000000000000033321372027637700205630ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2014 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_REPOCONF_INTERNAL_H__ #define __LR_REPOCONF_INTERNAL_H__ #include #include "types.h" #include "repoconf.h" G_BEGIN_DECLS /** Single repo file */ struct _LrYumRepoFile { gchar *path; /*!< Path to the file */ GKeyFile *keyfile; /*!< Parsed content */ gboolean modified; /*!< Unsaved modifications? */ }; typedef struct _LrYumRepoFile LrYumRepoFile; /** Yum repo config */ struct _LrYumRepoConf { LrYumRepoFile *file; /*!< Config file */ gchar *id; /*!< Repo ID (group name in key file) */ }; /** List of repoconfigs */ struct _LrYumRepoConfs { GSList *repos; /*!< List of LrYumRepoConf */ GSList *files; /*!< List of LrYumRepoFile */ }; G_END_DECLS #endif librepo-1.12.1/librepo/repomd.c000066400000000000000000000407511372027637700163430ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "repomd.h" #include "xmlparser_internal.h" #include "rcodes.h" #include "util.h" #define CHUNK_SIZE 8192 #define CONTENT_REALLOC_STEP 256 /* Repomd object manipulation helpers */ static LrYumRepoMdRecord * lr_yum_repomdrecord_init(const char *type) { LrYumRepoMdRecord *record = lr_malloc0(sizeof(*record)); record->chunk = g_string_chunk_new(128); record->type = lr_string_chunk_insert(record->chunk, type); return record; } static void lr_yum_repomdrecord_free(LrYumRepoMdRecord *rec) { if (!rec) return; g_string_chunk_free(rec->chunk); lr_free(rec); } LrYumRepoMd * lr_yum_repomd_init(void) { LrYumRepoMd *repomd = lr_malloc0(sizeof(*repomd)); repomd->chunk = g_string_chunk_new(32); return repomd; } void lr_yum_repomd_free(LrYumRepoMd *repomd) { if (!repomd) return; g_slist_free_full(repomd->records, (GDestroyNotify) lr_yum_repomdrecord_free); g_slist_free(repomd->repo_tags); g_slist_free(repomd->content_tags); g_slist_free_full(repomd->distro_tags, (GDestroyNotify) g_free); g_string_chunk_free(repomd->chunk); g_free(repomd); } static void lr_yum_repomd_set_record(LrYumRepoMd *repomd, LrYumRepoMdRecord *record) { if (!repomd || !record) return; repomd->records = g_slist_append(repomd->records, record); } static void lr_yum_repomd_set_revision(LrYumRepoMd *repomd, const char *revision) { if (!repomd) return; repomd->revision = lr_string_chunk_insert(repomd->chunk, revision); } static void lr_yum_repomd_add_repo_tag(LrYumRepoMd *repomd, char *tag) { assert(repomd); if (!tag) return; repomd->repo_tags = g_slist_append(repomd->repo_tags, g_string_chunk_insert(repomd->chunk, tag)); } static void lr_yum_repomd_add_content_tag(LrYumRepoMd *repomd, char *tag) { assert(repomd); if (!tag) return; repomd->content_tags = g_slist_append(repomd->content_tags, g_string_chunk_insert(repomd->chunk, tag)); } static void lr_yum_repomd_add_distro_tag(LrYumRepoMd *repomd, const char *cpeid, const char *tag) { assert(repomd); if (!tag) return; LrYumDistroTag *distrotag = lr_malloc0(sizeof(*distrotag)); distrotag->cpeid = lr_string_chunk_insert(repomd->chunk, cpeid); distrotag->tag = g_string_chunk_insert(repomd->chunk, tag); repomd->distro_tags = g_slist_append(repomd->distro_tags, distrotag); } LrYumRepoMdRecord * lr_yum_repomd_get_record(LrYumRepoMd *repomd, const char *type) { assert(repomd); assert(type); for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) { LrYumRepoMdRecord *record = elem->data; assert(record); if (!g_strcmp0(record->type, type)) return record; } return NULL; } gint64 lr_yum_repomd_get_highest_timestamp(LrYumRepoMd *repomd, GError **err) { gint64 max = 0; assert(repomd); assert(!err || *err == NULL); if (!repomd->records) { g_set_error(err, LR_REPOMD_ERROR, LRE_REPOMD, "repomd.xml has no records"); return max; } for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) { LrYumRepoMdRecord *record = elem->data; assert(record); if (max < record->timestamp) max = record->timestamp; } return max; } // repomd.xml parser typedef enum { STATE_START, STATE_REPOMD, STATE_REVISION, STATE_REPOID, STATE_TAGS, STATE_REPO, STATE_CONTENT, STATE_DISTRO, STATE_DATA, STATE_LOCATION, STATE_CHECKSUM, STATE_OPENCHECKSUM, STATE_HEADERCHECKSUM, STATE_TIMESTAMP, STATE_SIZE, STATE_OPENSIZE, STATE_HEADERSIZE, STATE_DBVERSION, NUMSTATES } LrRepomdState; /* NOTE: Same states in the first column must be together!!! * Performance tip: More frequent elements should be listed * first in its group (eg: element "package" (STATE_PACKAGE) * has a "file" element listed first, because it is more frequent * than a "version" element). */ static LrStatesSwitch stateswitches[] = { { STATE_START, "repomd", STATE_REPOMD, 0 }, { STATE_REPOMD, "revision", STATE_REVISION, 1 }, { STATE_REPOMD, "repoid", STATE_REPOID, 1 }, { STATE_REPOMD, "tags", STATE_TAGS, 0 }, { STATE_REPOMD, "data", STATE_DATA, 0 }, { STATE_TAGS, "repo", STATE_REPO, 1 }, { STATE_TAGS, "content", STATE_CONTENT, 1 }, { STATE_TAGS, "distro", STATE_DISTRO, 1 }, { STATE_DATA, "location", STATE_LOCATION, 0 }, { STATE_DATA, "checksum", STATE_CHECKSUM, 1 }, { STATE_DATA, "open-checksum", STATE_OPENCHECKSUM, 1 }, { STATE_DATA, "header-checksum", STATE_HEADERCHECKSUM, 1 }, { STATE_DATA, "timestamp", STATE_TIMESTAMP, 1 }, { STATE_DATA, "size", STATE_SIZE, 1 }, { STATE_DATA, "open-size", STATE_OPENSIZE, 1 }, { STATE_DATA, "header-size", STATE_HEADERSIZE, 1 }, { STATE_DATA, "database_version", STATE_DBVERSION, 1 }, { NUMSTATES, NULL, NUMSTATES, 0 } }; static void lr_start_handler(void *pdata, const xmlChar *xmlElement, const xmlChar **xmlAttr) { LrParserData *pd = pdata; LrStatesSwitch *sw; const char **attr = (const char **)xmlAttr; const char *element = (const char *)xmlElement; if (pd->err) return; // There was an error -> do nothing if (pd->depth != pd->statedepth) { // We are inside of unknown element pd->depth++; return; } pd->depth++; if (!pd->swtab[pd->state]) { // Current element should not have any sub elements return; } // Find current state by its name for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) if (!strcmp(element, sw->ename)) break; if (sw->from != pd->state) { // No state for current element (unknown element) lr_xml_parser_warning(pd, LR_XML_WARNING_UNKNOWNTAG, "Unknown element \"%s\"", element); return; } // Update parser data pd->state = sw->to; pd->docontent = sw->docontent; pd->statedepth = pd->depth; pd->lcontent = 0; pd->content[0] = '\0'; const char *val; switch(pd->state) { case STATE_START: break; case STATE_REPOMD: pd->repomdfound = TRUE; break; case STATE_REVISION: case STATE_TAGS: case STATE_REPO: case STATE_CONTENT: break; case STATE_REPOID: assert(pd->repomd); assert(!pd->repomdrecord); val = lr_find_attr("type", attr); if (val) pd->repomd->repoid_type = g_string_chunk_insert(pd->repomd->chunk, val); break; case STATE_DISTRO: assert(pd->repomd); assert(!pd->repomdrecord); val = lr_find_attr("cpeid", attr); if (val) pd->cpeid = g_strdup(val); break; case STATE_DATA: assert(pd->repomd); assert(!pd->repomdrecord); val = lr_find_attr("type", attr); if (!val) { lr_xml_parser_warning(pd, LR_XML_WARNING_MISSINGATTR, "Missing attribute \"type\" of a data element"); val = "unknown"; } pd->repomdrecord = lr_yum_repomdrecord_init(val); lr_yum_repomd_set_record(pd->repomd, pd->repomdrecord); break; case STATE_LOCATION: assert(pd->repomd); assert(pd->repomdrecord); val = lr_find_attr("href", attr); if (val) pd->repomdrecord->location_href = g_string_chunk_insert( pd->repomdrecord->chunk, val); else lr_xml_parser_warning(pd, LR_XML_WARNING_MISSINGATTR, "Missing attribute \"href\" of a location element"); val = lr_find_attr("xml:base", attr); if (val) pd->repomdrecord->location_base = g_string_chunk_insert( pd->repomdrecord->chunk, val); break; case STATE_CHECKSUM: assert(pd->repomd); assert(pd->repomdrecord); val = lr_find_attr("type", attr); if (!val) { lr_xml_parser_warning(pd, LR_XML_WARNING_MISSINGATTR, "Missing attribute \"type\" of a checksum element"); break; } pd->repomdrecord->checksum_type = g_string_chunk_insert( pd->repomdrecord->chunk, val); break; case STATE_OPENCHECKSUM: assert(pd->repomd); assert(pd->repomdrecord); val = lr_find_attr("type", attr); if (!val) { lr_xml_parser_warning(pd, LR_XML_WARNING_MISSINGATTR, "Missing attribute \"type\" of an open checksum element"); break; } pd->repomdrecord->checksum_open_type = g_string_chunk_insert( pd->repomdrecord->chunk, val); break; case STATE_HEADERCHECKSUM: assert(pd->repomd); assert(pd->repomdrecord); val = lr_find_attr("type", attr); if (!val) { lr_xml_parser_warning(pd, LR_XML_WARNING_MISSINGATTR, "Missing attribute \"type\" of an open checksum element"); break; } pd->repomdrecord->header_checksum_type = g_string_chunk_insert( pd->repomdrecord->chunk, val); break; case STATE_TIMESTAMP: case STATE_SIZE: case STATE_OPENSIZE: case STATE_HEADERSIZE: case STATE_DBVERSION: default: break; } } static void lr_end_handler(void *pdata, G_GNUC_UNUSED const xmlChar *element) { LrParserData *pd = pdata; unsigned int state = pd->state; if (pd->err) return; // There was an error -> do nothing if (pd->depth != pd->statedepth) { // Back from the unknown state pd->depth--; return; } pd->depth--; pd->statedepth--; pd->state = pd->sbtab[pd->state]; pd->docontent = 0; switch (state) { case STATE_START: case STATE_REPOMD: break; case STATE_REVISION: assert(pd->repomd); assert(!pd->repomdrecord); if (pd->lcontent == 0) { lr_xml_parser_warning(pd, LR_XML_WARNING_MISSINGVAL, "Missing value of a revision element"); break; } lr_yum_repomd_set_revision(pd->repomd, pd->content); break; case STATE_REPOID: assert(pd->repomd); assert(!pd->repomdrecord); pd->repomd->repoid = g_string_chunk_insert(pd->repomd->chunk, pd->content); break; case STATE_TAGS: break; case STATE_REPO: assert(pd->repomd); assert(!pd->repomdrecord); lr_yum_repomd_add_repo_tag(pd->repomd, pd->content); break; case STATE_CONTENT: assert(pd->repomd); assert(!pd->repomdrecord); lr_yum_repomd_add_content_tag(pd->repomd, pd->content); break; case STATE_DISTRO: assert(pd->repomd); assert(!pd->repomdrecord); lr_yum_repomd_add_distro_tag(pd->repomd, pd->cpeid, pd->content); if (pd->cpeid) { g_free(pd->cpeid); pd->cpeid = NULL; } break; case STATE_DATA: assert(pd->repomd); assert(pd->repomdrecord); pd->repomdrecord = NULL; break; case STATE_LOCATION: break; case STATE_CHECKSUM: assert(pd->repomd); assert(pd->repomdrecord); pd->repomdrecord->checksum = lr_string_chunk_insert( pd->repomdrecord->chunk, pd->content); break; case STATE_OPENCHECKSUM: assert(pd->repomd); assert(pd->repomdrecord); pd->repomdrecord->checksum_open = lr_string_chunk_insert( pd->repomdrecord->chunk, pd->content); break; case STATE_HEADERCHECKSUM: assert(pd->repomd); assert(pd->repomdrecord); pd->repomdrecord->header_checksum = lr_string_chunk_insert( pd->repomdrecord->chunk, pd->content); break; case STATE_TIMESTAMP: assert(pd->repomd); assert(pd->repomdrecord); pd->repomdrecord->timestamp = lr_xml_parser_strtoll(pd, pd->content, 0); break; case STATE_SIZE: assert(pd->repomd); assert(pd->repomdrecord); pd->repomdrecord->size = lr_xml_parser_strtoll(pd, pd->content, 0); break; case STATE_OPENSIZE: assert(pd->repomd); assert(pd->repomdrecord); pd->repomdrecord->size_open = lr_xml_parser_strtoll(pd, pd->content, 0); break; case STATE_HEADERSIZE: assert(pd->repomd); assert(pd->repomdrecord); pd->repomdrecord->size_header = lr_xml_parser_strtoll(pd, pd->content, 0); break; case STATE_DBVERSION: assert(pd->repomd); assert(pd->repomdrecord); pd->repomdrecord->db_version = (int) lr_xml_parser_strtoll(pd, pd->content, 0); break; default: break; } } gboolean lr_yum_repomd_parse_file(LrYumRepoMd *repomd, int fd, LrXmlParserWarningCb warningcb, void *warningcb_data, GError **err) { gboolean ret = TRUE; LrParserData *pd; XmlParser parser; GError *tmp_err = NULL; assert(fd >= 0); assert(repomd); assert(!err || *err == NULL); // Init memset(&parser, 0, sizeof(parser)); parser.startElement = lr_start_handler; parser.endElement = lr_end_handler; parser.characters = lr_char_handler; pd = lr_xml_parser_data_new(NUMSTATES); pd->parser = &parser; pd->state = STATE_START; pd->repomd = repomd; pd->warningcb = warningcb; pd->warningcb_data = warningcb_data; for (LrStatesSwitch *sw = stateswitches; sw->from != NUMSTATES; sw++) { if (!pd->swtab[sw->from]) pd->swtab[sw->from] = sw; pd->sbtab[sw->to] = sw->from; } // Parsing ret = lr_xml_parser_generic(parser, pd, fd, &tmp_err); if (tmp_err) g_propagate_error(err, tmp_err); // Check of results if (!tmp_err && !pd->repomdfound) { ret = FALSE; g_set_error(err, LR_XML_PARSER_ERROR, LRE_REPOMDXML, "Element was not found - Bad repomd file"); } // Clean up lr_xml_parser_data_free(pd); return ret; } librepo-1.12.1/librepo/repomd.h000066400000000000000000000106221372027637700163420ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_REPOMD_H__ #define __LR_REPOMD_H__ #include #include "xmlparser.h" #include "types.h" G_BEGIN_DECLS /** \defgroup repomd Repomd (repomd.xml) parser * \addtogroup repomd * @{ */ /** Yum repomd distro tag. */ typedef struct { char *cpeid; /*!< Tag cpeid value or NULL. */ char *tag; /*!< Tag value. */ } LrYumDistroTag; /** Yum repomd record. */ typedef struct { char *type; /*!< Type of record (e.g. "primary") */ char *location_href; /*!< Location href attribute */ char *location_base; /*!< Location base attribute */ char *checksum; /*!< Checksum value */ char *checksum_type; /*!< Type of checksum */ char *checksum_open; /*!< Checksum of uncompressed file */ char *checksum_open_type; /*!< Type of checksum of uncompressed file */ char *header_checksum; /*!< Checksum of zchunk header */ char *header_checksum_type; /*!< Type of checksum of zchunk header */ gint64 timestamp; /*!< File timestamp */ gint64 size; /*!< File size */ gint64 size_open; /*!< Size of uncompressed file */ gint64 size_header; /*!< Size of zchunk header */ int db_version; /*!< Version of database */ GStringChunk *chunk; /*!< String chunk */ } LrYumRepoMdRecord; /** Yum repomd.xml. */ typedef struct { char *revision; /*!< Revision string*/ char *repoid; /*!< RepoId */ char *repoid_type; /*!< RepoId type ("sha256", ...) */ GSList *repo_tags; /*!< List of strings */ GSList *content_tags; /*!< List of strings */ GSList *distro_tags; /*!< List of LrYumDistroTag* */ GSList *records; /*!< List with LrYumRepoMdRecords */ GStringChunk *chunk; /*!< String chunk for repomd strings (Note: LrYumRepomdRecord strings are stored in LrYumRepomdRecord->chunk) */ } LrYumRepoMd; /** Create new empty repomd object. * @return New repomd object. */ LrYumRepoMd * lr_yum_repomd_init(void); /** Free repomd content and repomd object itself. * @param repomd Repomd object. */ void lr_yum_repomd_free(LrYumRepoMd *repomd); /** Parse repomd.xml file. * @param repomd Empty repomd object. * @param fd File descriptor. * @param warningcb Callback for warnings * @param warningcb_data Warning callback user data * @param err GError ** * @return TRUE if everything is ok, FALSE if err is set. */ gboolean lr_yum_repomd_parse_file(LrYumRepoMd *repomd, int fd, LrXmlParserWarningCb warningcb, void *warningcb_data, GError **err); /** Get repomd record from the repomd object. * @param repomd Repomd record. * @param type Type of record e.g. "primary", "filelists", ... * @return Record of desired type or NULL. */ LrYumRepoMdRecord * lr_yum_repomd_get_record(LrYumRepoMd *repomd, const char *type); /** Get the highest timestamp from repomd records. * @param repomd Repomd record. * @param err GError ** * @return The highest timestamp from repomd records * or -1 if no records available. */ gint64 lr_yum_repomd_get_highest_timestamp(LrYumRepoMd *repomd, GError **err); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/repoutil_yum.c000066400000000000000000000064671372027637700176200ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _XOPEN_SOURCE 600 #include #include #include #include #include #include #include #include #include "rcodes.h" #include "util.h" #include "repomd.h" #include "yum.h" #include "handle.h" #include "result.h" #include "result_internal.h" gboolean lr_repoutil_yum_check_repo(const char *path, GError **err) { gboolean ret; LrHandle *h; LrResult *result; const char *urls[] = { path, NULL }; assert(path); assert(!err || *err == NULL); h = lr_handle_init(); if (!lr_handle_setopt(h, err, LRO_REPOTYPE, LR_YUMREPO)) { lr_handle_free(h); return FALSE; } if (!lr_handle_setopt(h, err, LRO_URLS, urls)) { lr_handle_free(h); return FALSE; } if (!lr_handle_setopt(h, err, LRO_CHECKSUM, 1)) { lr_handle_free(h); return FALSE; } if (!lr_handle_setopt(h, err, LRO_LOCAL, 1)) { lr_handle_free(h); return FALSE; } result = lr_result_init(); ret = lr_handle_perform(h, result, err); lr_result_free(result); lr_handle_free(h); return ret; } gboolean lr_repoutil_yum_parse_repomd(const char *in_path, LrYumRepoMd *repomd, GError **err) { int fd; gboolean ret; struct stat st; char *path; assert(in_path); assert(!err || *err == NULL); if (stat(in_path, &st) != 0) { g_set_error(err, LR_REPOUTIL_YUM_ERROR, LRE_IO, "stat(%s,) error: %s", in_path, g_strerror(errno)); return FALSE; } if (st.st_mode & S_IFDIR) path = lr_pathconcat(in_path, "repodata/repomd.xml", NULL); else path = g_strdup(in_path); fd = open(path, O_RDONLY); if (fd < 0) { g_set_error(err, LR_REPOUTIL_YUM_ERROR, LRE_IO, "open(%s, O_RDONLY) error: %s", path, g_strerror(errno)); lr_free(path); return FALSE; } lr_free(path); ret = lr_yum_repomd_parse_file(repomd, fd, NULL, NULL, err); close(fd); return ret; } double lr_yum_repomd_get_age(LrResult *result) { assert(result); if (!result->yum_repo || !result->yum_repo->repomd) return 0.0; int rc; struct stat st; rc = stat(result->yum_repo->repomd, &st); if (rc != 0) return 0.0; return difftime(time(NULL), st.st_mtime); } librepo-1.12.1/librepo/repoutil_yum.h000066400000000000000000000041151372027637700176110ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_REPOUTIL_YUM_H__ #define __LR_REPOUTIL_YUM_H__ #include #include "repomd.h" G_BEGIN_DECLS /** \defgroup repoutil_yum Yum repo high level function * \addtogroup repoutil_yum * @{ */ /** Check checksum of selected repository. * @param path Path to directory containing "repodata" subdir. * @param err GError ** * @return TRUE is everything is ok, FALSE if err is set. */ gboolean lr_repoutil_yum_check_repo(const char *path, GError **err); /** Parse repomd.xml file. * @param path Path to repository or to the repomd file * @param repomd Empty repomd object * @param err GError ** * @return TRUE is everything is ok, FALSE if err is set. */ gboolean lr_repoutil_yum_parse_repomd(const char *path, LrYumRepoMd *repomd, GError **err); /** Return age of the repomd.xml (based on mtime of the file * and the current time) * @param result Result object * @return Age of the file (number of seconds since last modification) */ double lr_yum_repomd_get_age(LrResult *result); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/result.c000066400000000000000000000061501372027637700163660ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "librepo.h" #include "util.h" #include "result.h" #include "handle_internal.h" #include "result_internal.h" #include "yum.h" #include "repomd.h" LrResult * lr_result_init(void) { return lr_malloc0(sizeof(struct _LrResult)); } void lr_result_clear(LrResult *result) { if (!result) return; lr_free(result->destdir); lr_yum_repomd_free(result->yum_repomd); lr_yum_repo_free(result->yum_repo); memset(result, 0, sizeof(struct _LrResult)); } void lr_result_free(LrResult *result) { if (!result) return; lr_result_clear(result); lr_free(result); } gboolean lr_result_getinfo(LrResult *result, GError **err, LrResultInfoOption option, ...) { gboolean rc = TRUE; va_list arg; GError *tmp_err = NULL; assert(!err || *err == NULL); if (!result) { g_set_error(err, LR_RESULT_ERROR, LRE_BADFUNCARG, "No result specified"); return FALSE; } va_start(arg, option); switch (option) { case LRR_RPMMD_REPO: case LRR_YUM_REPO: { LrYumRepo **repo; repo = va_arg(arg, LrYumRepo **); *repo = result->yum_repo; break; } case LRR_RPMMD_REPOMD: case LRR_YUM_REPOMD: { LrYumRepoMd **repomd = va_arg(arg, LrYumRepoMd **); *repomd = result->yum_repomd; break; } case LRR_RPMMD_TIMESTAMP: case LRR_YUM_TIMESTAMP: { gint64 *ts = va_arg(arg, gint64 *); if (result->yum_repomd) { *ts = lr_yum_repomd_get_highest_timestamp(result->yum_repomd, &tmp_err); if (tmp_err) { rc = FALSE; g_propagate_error(err, tmp_err); } } else { *ts = 0; rc = FALSE; g_set_error(err, LR_RESULT_ERROR, LRE_REPOMD, "No repomd data available - cannot get a timestamp"); } break; } default: rc = FALSE; g_set_error(err, LR_RESULT_ERROR, LRE_UNKNOWNOPT, "Unknown option"); break; } va_end(arg); return rc; } librepo-1.12.1/librepo/result.h000066400000000000000000000052411372027637700163730ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_RESULT_H__ #define __LR_RESULT_H__ #include #include "types.h" G_BEGIN_DECLS /** \defgroup result Result object * \addtogroup result * @{ */ /** Result object */ typedef struct _LrResult LrResult; /** Result options for ::lr_result_getinfo. */ typedef enum { LRR_YUM_REPO, /*!< (LrYumRepo *) Reference to ::LrYumRepo in result */ LRR_YUM_REPOMD, /*!< (LrYumRepoMd *) Reference to ::LrYumRepoMd in result */ LRR_YUM_TIMESTAMP, /*!< (gint64) The highest timestamp from repomd.xml. See: https://github.com/Tojaj/librepo/issues/25 See: http://yum.baseurl.org/gitweb?p=yum.git;a=commitdiff;h=59d3d67f */ LRR_RPMMD_REPO, /*!< In C same as LRR_YUM_REPO */ LRR_RPMMD_REPOMD, /*!< In C same as LRR_YUM_REPOMD */ LRR_RPMMD_TIMESTAMP, /*!< In C same as LRR_YUM_TIMESTAMP */ LRR_SENTINEL, } LrResultInfoOption; /** Return new allocated ::LrResult object * @return New allocated ::LrResult object */ LrResult * lr_result_init(void); /** Clean result object. * @param result Result object. */ void lr_result_clear(LrResult *result); /** Free result object. * @param result Result object. */ void lr_result_free(LrResult *result); /** Get information about downloaded/localised repository from result object. * @param result Result object. * @param err GError ** * @param option Option from ::LrResultInfoOption enum. * @param ... Appropriate variable for the selected option. * @return TRUE if everything is ok, false if err is set. */ gboolean lr_result_getinfo(LrResult *result, GError **err, LrResultInfoOption option, ...); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/result_internal.h000066400000000000000000000025171372027637700202720ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_RESULT_INTERNAL_H__ #define __LR_RESULT_INTERNAL_H__ #include #include "repomd.h" #include "yum.h" G_BEGIN_DECLS struct _LrResult { char *destdir; /*!< Directory with repo */ LrYumRepoMd *yum_repomd; /*!< Pointer to struct representingrepomd.xml */ LrYumRepo *yum_repo; /*!< Pointer to struct with info about yum repo */ }; G_END_DECLS #endif librepo-1.12.1/librepo/types.h000066400000000000000000000202531372027637700162210ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_TYPES_H__ #define __LR_TYPES_H__ #include G_BEGIN_DECLS /** \defgroup types Basic types and constants * \addtogroup types * @{ */ /** Handle object containing configration for repository metadata and * package downloading. */ /** Flags for available checks. */ typedef enum { LR_CHECK_GPG = (1<<0), /*!< GPG check */ LR_CHECK_CHECKSUM = (1<<1), /*!< Checksum check */ } LrChecks; /** Repo types flags. */ typedef enum { LR_YUMREPO = (1<<1), /*!< Yum repository */ LR_RPMMDREPO = LR_YUMREPO, LR_SUSEREPO = (1<<2), /*!< YaST2 repository - Not implemented yet */ LR_DEBREPO = (1<<3), /*!< Debian repository - Not implemented yet */ } LrRepotype; /** Proxy types. */ typedef enum { LR_PROXY_HTTP, /*!< HTTP proxy (Default) */ LR_PROXY_HTTP_1_0, /*!< HTTP 1.0 proxy */ LR_PROXY_SOCKS4, /*!< SOCKS4 proxy */ LR_PROXY_SOCKS5, /*!< SOCKS5 proxy */ LR_PROXY_SOCKS4A, /*!< SOCKS4A proxy */ LR_PROXY_SOCKS5_HOSTNAME, /*!< SOCKS5 proxy */ } LrProxyType; /** IpResolve types */ typedef enum { LR_IPRESOLVE_WHATEVER, /*!< Default - resolves addresses to all IP versions */ LR_IPRESOLVE_V4, /*!< Resolve to IPv4 addresses */ LR_IPRESOLVE_V6, /*!< Resolve to IPv6 addresses */ } LrIpResolveType; /** LrAuth methods */ typedef enum { LR_AUTH_NONE = 0, /*!< None auth method */ LR_AUTH_BASIC = (1<<0), /*!< HTTP Basic authentication (Default) */ LR_AUTH_DIGEST = (1<<1), /*!< HTTP Digest authentication */ LR_AUTH_NEGOTIATE = (1<<2), /*!< HTTP Negotiate (SPNEGO) authentication */ LR_AUTH_NTLM = (1<<3), /*!< HTTP NTLM authentication */ LR_AUTH_DIGEST_IE = (1<<4), /*!< HTTP Digest authentication with an IE flavor */ LR_AUTH_NTLM_WB = (1<<5), /*!< NTLM delegating to winbind helper */ LR_AUTH_ONLY = (1<<31), /*!< This is a meta symbol. OR this value together with a single specific auth value to force libcurl to probe for un-restricted auth and if not, only that single auth algorithm is acceptable. */ LR_AUTH_ANY = (~LR_AUTH_DIGEST_IE), /*!< All suitable methods */ } LrAuth; /* Some common used arrays for LRO_YUMDLIST */ /** Predefined value for LRO_YUMDLIST option - Download whole repo. */ #define LR_YUM_FULL NULL #define LR_RPMMD_FULL NULL /** Predefined value for LRO_YUMDLIST option - Download only repomd.xml. */ #define LR_YUM_REPOMDONLY {NULL} #define LR_RPMMD_REPOMDONLY {NULL} /** Predefined value for LRO_YUMDLIST option - Download only base xml files. */ #define LR_YUM_BASEXML {"primary", "filelists", "other", NULL} #define LR_RPMMD_BASEXML {"primary", "filelists", "other", NULL} /** Predefined value for LRO_YUMDLIST option - Download only base db files. */ #define LR_YUM_BASEDB {"primary_db", "filelists_db", "other_db", NULL} #define LR_RPMMD_BASEDB {"primary_db", "filelists_db", "other_db", NULL} /** Predefined value for LRO_YUMDLIST option - Download only primary, * filelists and prestodelta. */ #define LR_YUM_HAWKEY {"primary", "filelists", "prestodelta", NULL} #define LR_RPMMD_HAWKEY {"primary", "filelists", "prestodelta", NULL} typedef enum LrCbReturnCode_e { LR_CB_OK = 0, /*!< All fine */ LR_CB_ABORT, /*!< Abort the transfer - if no failfast is set, then, it just abort the current download */ LR_CB_ERROR, /*!< Error - Fatal error, abort all downloading and return from the download function (e.g. lr_download_packages, ...) */ } LrCbReturnCode; /** Progress callback prototype * @param clientp Pointer to user data. * @param total_to_download Total number of bytes to download * @param now_downloaded Number of bytes currently downloaded * @return See LrCbReturnCode codes */ typedef int (*LrProgressCb)(void *clientp, double total_to_download, double now_downloaded); /** Transfer status codes */ typedef enum { LR_TRANSFER_SUCCESSFUL, LR_TRANSFER_ALREADYEXISTS, LR_TRANSFER_ALREDYEXISTS = LR_TRANSFER_ALREADYEXISTS, // Deprecated - Backward comp. - remove in future release LR_TRANSFER_ERROR, } LrTransferStatus; /** Called when a transfer is done (use transfer status to check * if successful or failed). * @param clientp Pointer to user data. * @param status Transfer status * @param msg Error message or NULL. * @return See LrCbReturnCode codes */ typedef int (*LrEndCb)(void *clientp, LrTransferStatus status, const char *msg); /** MirrorFailure callback prototype * @param clientp Pointer to user data. * @param msg Error message. * @param url Mirror URL * @return See LrCbReturnCode codes */ typedef int (*LrMirrorFailureCb)(void *clientp, const char *msg, const char *url); /** MirrorFailure callback * @param clientp Pointer to user data. * @param msg Error message. * @param url Mirror URL * @param metadata Metadata type "primary", etc. * @return See LrCbReturnCode codes */ typedef int (*LrHandleMirrorFailureCb)(void *clientp, const char *msg, const char *url, const char *metadata); typedef enum { LR_FMSTAGE_INIT, /*!< Fastest mirror detection just started. ptr is NULL*/ LR_FMSTAGE_CACHELOADING, /*!< ptr is (char *) pointer to string with path to the cache file. (Do not modify or free the string). */ LR_FMSTAGE_CACHELOADINGSTATUS, /*!< if cache was loaded successfully, ptr is NULL, otherwise ptr is (char *) string with error message. (Do not modify or free the string) */ LR_FMSTAGE_DETECTION, /*!< Detection (pinging) in progress. If all data was loaded from cache, this stage is skipped. ptr is pointer to long. This is the number of how much mirrors have to be "pinged" */ LR_FMSTAGE_FINISHING, /*!< Detection is done, sorting mirrors, updating cache, etc. ptr is NULL */ LR_FMSTAGE_STATUS, /*!< The very last invocation of fastest mirror callback. If fastest mirror detection was successful ptr is NULL, otherwise ptr contain (char *) string with error message. (Do not modify or free the string) */ } LrFastestMirrorStages; /** Fastest mirror status callback * @param clientp Pointer to user data. * @param stage Stage of fastest mirror detection. * @param ptr Value specific for each stage of detection. */ typedef void (*LrFastestMirrorCb)(void *clientp, LrFastestMirrorStages stage, void *ptr); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/url_substitution.c000066400000000000000000000105361372027637700205110ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "url_substitution.h" #include "util.h" static LrVar * lr_var_new(const char *var, const char *val) { LrVar *var_val = lr_malloc0(sizeof(LrVar)); var_val->var = g_strdup(var); var_val->val = g_strdup(val); return var_val; } static void lr_var_free(LrVar *var) { lr_free(var->var); lr_free(var->val); lr_free(var); } LrUrlVars * lr_urlvars_set(LrUrlVars *list, const char *var, const char *value) { LrUrlVars *ret = list; assert(var); if (!value) { // Remove var from the list for (LrUrlVars *elem = list; elem; elem = g_slist_next(elem)) { LrVar *var_val = elem->data; if (!strcmp(var, var_val->var)) { ret = g_slist_remove(list, var_val); lr_var_free(var_val); return ret; } } } else { // Replace var for (LrUrlVars *elem = list; elem; elem = g_slist_next(elem)) { LrVar *var_val = elem->data; if (!strcmp(var, var_val->var)) { lr_free(var_val->val); var_val->val = g_strdup(value); return ret; } } // Add var LrVar *var_val = lr_var_new(var, value); ret = g_slist_prepend(list, var_val); } return ret; } void lr_urlvars_free(LrUrlVars *list) { if (!list) return; for (LrUrlVars *elem = list; elem; elem = g_slist_next(elem)) lr_var_free(elem->data); g_slist_free(list); } char * lr_url_substitute(const char *url, LrUrlVars *list) { const char *cur = url; const char *p = url; if (!url) return NULL; if (!list) return g_strdup(url); char *res = g_strdup(""); while (*cur != '\0') { if (*cur == '$') { // Adds unprocessed text before the variable to the "res". if (cur-p) { char *tmp = g_strndup(p, cur-p); char *tmp_res = g_strconcat(res, tmp, NULL); g_free(tmp); g_free(res); res = tmp_res; p = cur; } // Tries to substitute the variable and store result to the "res". gboolean bracket; if (*++cur == '{') { bracket = TRUE; ++cur; } else { bracket = FALSE; } const char *varname = cur; for (; isalnum(*cur) || *cur == '_'; ++cur); if (cur != varname && (!bracket || *cur == '}')) { for (LrUrlVars *elem = list; elem; elem = g_slist_next(elem)) { LrVar *var_val = elem->data; size_t var_len = strlen(var_val->var); if (var_len == cur - varname && strncmp(var_val->var, varname, var_len) == 0) { if (bracket) ++cur; p = cur; char *tmp_res = g_strconcat(res, var_val->val, NULL); g_free(res); res = tmp_res; break; } } } } else { ++cur; } } // Adds remaining text to the "res". if (*p != '\0') { char *tmp_res = g_strconcat(res, p, NULL); g_free(res); res = tmp_res; } return res; } librepo-1.12.1/librepo/url_substitution.h000066400000000000000000000047511372027637700205200ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_URL_SUBSTITUTION_H__ #define __LR_URL_SUBSTITUTION_H__ #include #include G_BEGIN_DECLS /** \defgroup url_substitution Substitution of variables in url * \addtogroup url_substitution * @{ */ /** Element of LrUrlVars list */ typedef struct { char *var; /*!< Variable name (without prefixed $ symbol) */ char *val; /*!< Value that will be placed instead of the variable */ } LrVar; /** LrUrlVars list is in fact GSList */ typedef GSList LrUrlVars; /** Set value of variable. Use variable names without '$' prefix. * If value is NULL, variable will be removed from the list. * If list is NULL, new list will be created. * @param list a GSList or NULL for the first item * @param var a variable name (must not be a NULL) * @param value a variable value * @return the new start of the GSList of url substitutions */ LrUrlVars * lr_urlvars_set(LrUrlVars *list, const char *var, const char *value); /** Frees all of the memory used by LrUrlVars. * @param list a list of substitutions */ void lr_urlvars_free(LrUrlVars *list); /** Substitute variables in the url. Returns a newly allocated string. * ${var_name} and $var_name syntaxes are supported. * var_name: [A-Za-z0-9_]+ * Non-exsistent variables are not substituted. * @param url a url * @param list a list of variables and its substitutions or NULL * @return a newly allocated string with substituted url */ char * lr_url_substitute(const char *url, LrUrlVars *list); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/util.c000066400000000000000000000431561372027637700160340ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _POSIX_C_SOURCE 200809L #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "version.h" #include "metalink.h" #include "cleanup.h" #include "yum.h" #define DIR_SEPARATOR "/" #define ENV_DEBUG "LIBREPO_DEBUG" #ifdef CURL_GLOBAL_ACK_EINTR #define EINTR_SUPPORT " with CURL_GLOBAL_ACK_EINTR support" #define CURL_GLOBAL_INIT_FLAGS CURL_GLOBAL_ALL|CURL_GLOBAL_ACK_EINTR #else #define EINTR_SUPPORT "" #define CURL_GLOBAL_INIT_FLAGS CURL_GLOBAL_ALL #endif static void lr_log_handler(G_GNUC_UNUSED const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level, const gchar *message, G_GNUC_UNUSED gpointer user_data) { g_fprintf(stderr, "%s\n", message); } static void lr_init_debugging(void) { if (!g_getenv(ENV_DEBUG)) return; g_log_set_handler("librepo", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, lr_log_handler, NULL); } void lr_log_librepo_summary(void) { _cleanup_free_ gchar *time = NULL; _cleanup_date_time_unref_ GDateTime *datetime = NULL; g_info("Librepo version: %d.%d.%d%s (%s)", LR_VERSION_MAJOR, LR_VERSION_MINOR, LR_VERSION_PATCH, EINTR_SUPPORT, curl_version()); datetime = g_date_time_new_now_local(); // Date+Time in ISO 8601 format time = g_date_time_format(datetime, "%Y-%m-%dT%H:%M:%S%z"); g_debug("Current date: %s", time); } static gpointer lr_init_once_cb(gpointer user_data G_GNUC_UNUSED) { curl_global_init((long) CURL_GLOBAL_INIT_FLAGS); lr_init_debugging(); lr_log_librepo_summary(); return GINT_TO_POINTER(1); } void lr_global_init(void) { static GOnce init_once = G_ONCE_INIT; g_once(&init_once, lr_init_once_cb, NULL); } /* void lr_global_cleanup() { curl_global_cleanup(); } */ void lr_out_of_memory(void) { fprintf(stderr, "Out of memory\n"); abort(); exit(1); } void * lr_malloc(size_t len) { void *m = malloc(len); if (!m) lr_out_of_memory(); return m; } void * lr_malloc0(size_t len) { void *m = calloc(1, len); if (!m) lr_out_of_memory(); return m; } void * lr_realloc(void *ptr, size_t len) { void *m = realloc(ptr, len); if (!m && len) lr_out_of_memory(); return m; } void lr_free(void *m) { if (m) free(m); } int lr_gettmpfile(void) { int fd; _cleanup_free_ char *template = NULL; template = g_build_filename(g_get_tmp_dir(), "librepo-tmp-XXXXXX", NULL); fd = mkstemp(template); if (fd < 0) { perror("Cannot create temporary file - mkstemp"); exit(1); } unlink(template); return fd; } char * lr_gettmpdir(void) { char *template = g_build_filename(g_get_tmp_dir(), "librepo-tmpdir-XXXXXX", NULL); if (!mkdtemp(template)) { lr_free(template); return NULL; } return template; } char * lr_pathconcat(const char *first, ...) { va_list args; const char *next; char *separator = DIR_SEPARATOR; char *chunk, *res = NULL; size_t separator_len = strlen(DIR_SEPARATOR); size_t total_len; // Maximal len of result size_t offset = 0; int is_first = 1; char *qmark_section; int previous_was_empty = 0; // If last chunk was "" then separator will be // appended to the result if (!first) return NULL; total_len = strlen(first); va_start(args, first); while ((chunk = va_arg(args, char *))) total_len += (strlen(chunk) + separator_len); va_end(args); if (total_len == 0) return g_strdup(""); qmark_section = strchr(first, '?'); res = lr_malloc(total_len + separator_len + 1); next = first; va_start(args, first); while (1) { const char *current, *start, *end; size_t current_len; if (next) { current = next; next = va_arg(args, char *); } else break; current_len = strlen(current); if (!current_len) { previous_was_empty = 1; continue; /* Skip empty element */ } else previous_was_empty = 0; start = current; end = start + current_len; if (is_first && qmark_section) end -= strlen(qmark_section); /* Skip leading separators - except first element */ if (separator_len && is_first == 0) { while (!strncmp(start, separator, separator_len)) start += separator_len; } /* Skip trailing separators */ if (separator_len) { while (start + separator_len <= end && !strncmp(end-separator_len, separator, separator_len)) end -= separator_len; } if (start >= end) { /* Element is filled only by separators */ if (is_first) is_first = 0; continue; } /* Prepend separator - except first element */ if (is_first == 0) { strncpy(res + offset, separator, separator_len); offset += separator_len; } else is_first = 0; strncpy(res + offset, start, end - start); offset += end - start; } va_end(args); if (qmark_section) { strcpy(res + offset, qmark_section); offset += strlen(qmark_section); } assert(offset <= total_len); if (offset == 0) { lr_free(res); return g_strdup(first); } /* If last element was emtpy string, append separator to the end */ if (previous_was_empty && is_first == 0) { strncpy(res + offset, separator, separator_len); offset += separator_len; } assert(offset <= total_len); res[offset] = '\0'; return res; } int lr_remove_dir_cb(const char *fpath, G_GNUC_UNUSED const struct stat *sb, G_GNUC_UNUSED int typeflag, G_GNUC_UNUSED struct FTW *ftwbuf) { int rv = remove(fpath); if (rv) g_warning("Cannot remove: %s: %s", fpath, g_strerror(errno)); return rv; } int lr_remove_dir(const char *path) { return nftw(path, lr_remove_dir_cb, 64, FTW_DEPTH | FTW_PHYS); } int lr_copy_content(int source, int dest) { const int bufsize = 2048; char buf[bufsize]; ssize_t size; lseek(source, 0, SEEK_SET); lseek(dest, 0, SEEK_SET); while ((size = read(source, buf, bufsize)) > 0) if (write(dest, buf, size) == -1) return -1; return (size < 0) ? -1 : 0; } char * lr_prepend_url_protocol(const char *path) { if (!path) return NULL; if (strstr(path, "://")) // Protocol was specified return g_strdup(path); if (g_str_has_prefix(path, "file:/")) return g_strdup(path); if (path[0] == '/') // Path is absolute path return g_strconcat("file://", path, NULL); char *path_with_protocol, *resolved_path = realpath(path, NULL); if (!resolved_path) { g_warning("Error resolving real path of %s: %s", path, g_strerror(errno)); return NULL; } path_with_protocol = g_strconcat("file://", resolved_path, NULL); free(resolved_path); return path_with_protocol; } gchar * lr_string_chunk_insert(GStringChunk *chunk, const gchar *string) { assert(chunk); if (!string) return NULL; return g_string_chunk_insert(chunk, string); } int lr_xml_parser_warning_logger(LrXmlParserWarningType type G_GNUC_UNUSED, char *msg, void *cbdata, GError **err G_GNUC_UNUSED) { g_warning("WARNING: %s: %s", (char *) cbdata, msg); return LR_CB_RET_OK; } gboolean lr_best_checksum(GSList *list, LrChecksumType *type, gchar **value) { if (!list) return FALSE; assert(type); assert(value); LrChecksumType tmp_type = LR_CHECKSUM_UNKNOWN; gchar *tmp_value = NULL; for (GSList *elem = list; elem; elem = g_slist_next(elem)) { LrMetalinkHash *hash = elem->data; if (!hash->type || !hash->value) continue; LrChecksumType ltype = lr_checksum_type(hash->type); if (ltype != LR_CHECKSUM_UNKNOWN && ltype > tmp_type) { tmp_type = ltype; tmp_value = hash->value; } } if (tmp_type != LR_CHECKSUM_UNKNOWN) { *type = tmp_type; *value = tmp_value; return TRUE; } return FALSE; } gchar * lr_url_without_path(const char *url) { if (!url) return NULL; // Filesystem if (g_str_has_prefix(url, "file:///")) return g_strdup("file://"); if (g_str_has_prefix(url, "file:/")) return g_strdup("file://"); // Skip protocol prefix (ftp://, http://, file://, etc.) gchar *ptr = strstr(url, "://"); if (ptr) ptr += 3; else ptr = (gchar *) url; // Find end of the host name while (*ptr != '\0' && *ptr != '/') ptr++; // Calculate length of hostname size_t len = ptr - url; gchar *host = g_strndup(url, len); //g_debug("%s: %s -> %s", __func__, url, host); return host; } gchar ** lr_strv_dup(gchar **array) { guint length; gchar **copy = NULL; GPtrArray *ptrarray = NULL; if (!array) return array; length = g_strv_length(array); ptrarray = g_ptr_array_sized_new(length + 1); for (guint x=0; x < length; x++) g_ptr_array_add(ptrarray, g_strdup(array[x])); g_ptr_array_add(ptrarray, NULL); copy = (gchar **) ptrarray->pdata; g_ptr_array_free(ptrarray, FALSE); return copy; } gboolean lr_is_local_path(const gchar *path) { if (!path || !*path) return FALSE; if (strstr(path, "://") && !g_str_has_prefix(path, "file://")) return FALSE; return TRUE; } gboolean lr_key_file_save_to_file(GKeyFile *keyfile, const gchar *filename, GError **err) { _cleanup_free_ gchar *content = NULL; gsize length; content = g_key_file_to_data(keyfile, &length, err); if (!content) return FALSE; return g_file_set_contents(filename, content, length, err); } #ifdef WITH_ZCHUNK LrChecksumType lr_checksum_from_zck_hash(zck_hash zck_checksum_type) { switch (zck_checksum_type) { case ZCK_HASH_SHA1: return LR_CHECKSUM_SHA1; case ZCK_HASH_SHA256: return LR_CHECKSUM_SHA256; default: return LR_CHECKSUM_UNKNOWN; } } zck_hash lr_zck_hash_from_lr_checksum(LrChecksumType checksum_type) { switch (checksum_type) { case LR_CHECKSUM_SHA1: return ZCK_HASH_SHA1; case LR_CHECKSUM_SHA256: return ZCK_HASH_SHA256; default: return ZCK_HASH_UNKNOWN; } } static zckCtx * init_zck_read(const char *checksum, LrChecksumType checksum_type, gint64 zck_header_size, int fd, GError **err) { assert(!err || *err == NULL); zckCtx *zck = zck_create(); if(!zck_init_adv_read(zck, fd)) { g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK, "Unable to initialize zchunk file for reading"); return FALSE; } zck_hash ct = lr_zck_hash_from_lr_checksum(checksum_type); if(ct == ZCK_HASH_UNKNOWN) { g_set_error(err, LR_YUM_ERROR, LRE_ZCK, "Zchunk doesn't support checksum type %i", checksum_type); free(zck); return NULL; } if(!zck_set_ioption(zck, ZCK_VAL_HEADER_HASH_TYPE, ct)) { g_set_error(err, LR_YUM_ERROR, LRE_ZCK, "Error setting validation checksum type"); free(zck); return NULL; } if(!zck_set_ioption(zck, ZCK_VAL_HEADER_LENGTH, zck_header_size)) { g_set_error(err, LR_YUM_ERROR, LRE_ZCK, "Error setting header size"); free(zck); return NULL; } if(!zck_set_soption(zck, ZCK_VAL_HEADER_DIGEST, checksum, strlen(checksum))) { g_set_error(err, LR_YUM_ERROR, LRE_ZCK, "Unable to set validation checksum: %s", checksum); free(zck); return NULL; } return zck; } zckCtx * lr_zck_init_read_base(const char *checksum, LrChecksumType checksum_type, gint64 zck_header_size, int fd, GError **err) { assert(!err || *err == NULL); lseek(fd, 0, SEEK_SET); zckCtx *zck = init_zck_read(checksum, checksum_type, zck_header_size, fd, err); if(zck == NULL) return NULL; if(!zck_read_lead(zck)) { g_set_error(err, LR_YUM_ERROR, LRE_ZCK, "Unable to read zchunk lead"); zck_free(&zck); return NULL; } if(!zck_read_header(zck)) { g_set_error(err, LR_YUM_ERROR, LRE_ZCK, "Unable to read zchunk header"); zck_free(&zck); return NULL; } return zck; } gboolean lr_zck_valid_header_base(const char *checksum, LrChecksumType checksum_type, gint64 zck_header_size, int fd, GError **err) { assert(!err || *err == NULL); lseek(fd, 0, SEEK_SET); zckCtx *zck = init_zck_read(checksum, checksum_type, zck_header_size, fd, err); if(zck == NULL) return FALSE; if(!zck_validate_lead(zck)) { g_set_error(err, LR_YUM_ERROR, LRE_ZCK, "Unable to read zchunk lead"); zck_free(&zck); return FALSE; } zck_free(&zck); return TRUE; } zckCtx * lr_zck_init_read(LrDownloadTarget *target, char *filename, int fd, GError **err) { zckCtx *zck = NULL; gboolean found = FALSE; for(GSList *cksum = target->checksums; cksum; cksum = g_slist_next(cksum)) { GError *tmp_err = NULL; LrDownloadTargetChecksum *ck = (LrDownloadTargetChecksum*)(cksum->data); g_debug("Checking checksum: %i: %s", ck->type, ck->value); zck = lr_zck_init_read_base(ck->value, ck->type, target->zck_header_size, fd, &tmp_err); if(zck == NULL) { g_debug("%s: Didn't find matching header in %s: %s", __func__, filename, tmp_err->message); g_clear_error(&tmp_err); continue; } g_debug("%s: Found matching header in %s", __func__, filename); found = TRUE; break; } if(!found) g_set_error(err, LR_YUM_ERROR, LRE_ZCK, "Zchunk header checksum didn't match expected checksum"); return zck; } gboolean lr_zck_valid_header(LrDownloadTarget *target, char *filename, int fd, GError **err) { assert(!err || *err == NULL); for(GSList *cksum = target->checksums; cksum; cksum = g_slist_next(cksum)) { GError *tmp_err = NULL; LrDownloadTargetChecksum *ck = (LrDownloadTargetChecksum*)(cksum->data); if(lr_zck_valid_header_base(ck->value, ck->type, target->zck_header_size, fd, &tmp_err)) { return TRUE; } g_clear_error(&tmp_err); } g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK, "%s's zchunk header doesn't match", filename); return FALSE; } #endif /* WITH_ZCHUNK */ gboolean lr_get_recursive_files_rec(char *path, char *extension, GSList **filelist, GError **err) { assert(!err || *err == NULL); assert(filelist); GDir *d = g_dir_open(path, 0, err); if(d == NULL) return FALSE; const char *file = NULL; while((file = g_dir_read_name(d))) { GError *tmp_err = NULL; char *fullpath = g_build_path("/", path, file, NULL); if(g_file_test(fullpath, G_FILE_TEST_IS_DIR)) { lr_get_recursive_files_rec(fullpath, extension, filelist, &tmp_err); if(tmp_err) { g_warning("Unable to read directory %s: %s", fullpath, tmp_err->message); g_clear_error(&tmp_err); } g_free(fullpath); } else if(g_file_test(fullpath, G_FILE_TEST_IS_REGULAR) && g_str_has_suffix(fullpath, extension)) { *filelist = g_slist_prepend(*filelist, fullpath); } else { g_free(fullpath); } } g_dir_close(d); return TRUE; } GSList * lr_get_recursive_files(char *path, char *extension, GError **err) { GSList *filelist = NULL; assert(!err || *err == NULL); if(!lr_get_recursive_files_rec(path, extension, &filelist, err)) { g_slist_free_full(filelist, free); return NULL; } return filelist; } librepo-1.12.1/librepo/util.h000066400000000000000000000244521372027637700160370ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_UTIL_H__ #define __LR_UTIL_H__ #include #include #include #include #ifdef WITH_ZCHUNK #include #endif /* WITH_ZCHUNK */ #include "checksum.h" #include "xmlparser.h" #include "downloadtarget.h" G_BEGIN_DECLS /** \defgroup util Utility functions and macros * \addtogroup util * @{ */ /** Macro for curl version check. * @param major Major version * @param minor Minor version * @param patch Patch version * @return True if current curl version is higher or equal */ #define LR_CURL_VERSION_CHECK(major,minor,patch) \ (LIBCURL_VERSION_MAJOR > (major) || \ (LIBCURL_VERSION_MAJOR == (major) && LIBCURL_VERSION_MINOR > (minor)) || \ (LIBCURL_VERSION_MAJOR == (major) && LIBCURL_VERSION_MINOR == (minor) && \ LIBCURL_VERSION_PATCH >= (patch))) /** Initialize librepo library. * This is called automatically to initialize librepo. * You normally don't have to call this function manually. */ void lr_global_init(void); /** Clean up librepo library. void lr_global_cleanup(); */ /** Log a debug message with Librepo version and current time. */ void lr_log_librepo_summary(void); /** Print "Out of memory" message to stderr and abort program execution. * This function is used when malloc call fails. */ void lr_out_of_memory(void); /** Allocate len bytes of memory. * @param len Number of bytes to be allocated. * @return Pointer to allocated memory. */ void *lr_malloc(size_t len); /** Allocate len bytes of memory. The allocated memory is set to zero. * @param len Number of bytes to be allocated. * @return Pointer to allocated memory. */ void *lr_malloc0(size_t len); /** Change size of block memory pointed by ptr to the new len. * @param ptr Pointer to block of memory or NULL. * @param len New len of the block. * @return New pointer to the reallocated memory. */ void *lr_realloc(void *ptr, size_t len); /** Free the memory block. * @param mem Pointer to block of memory. */ void lr_free(void *mem); /** Create temporary librepo file in /tmp directory. * @return File descriptor. */ int lr_gettmpfile(void); /** Create temporary directory in /tmp directory. * @return Path to directory. */ char *lr_gettmpdir(void); /** Concatenate all of given part of path. * If last chunk is "" then separator will be appended to the result. * @param str First part of the path. * @param ... NULL terminated list of strings. * @return Concatenated path. */ char *lr_pathconcat(const char *str, ...) G_GNUC_NULL_TERMINATED; /** Recursively remove directory. * @param path Path to the directory. * @return 0 on succes, -1 on error. */ int lr_remove_dir(const char *path); /** Copy content from source file descriptor to the dest file descriptor. * @param source Source opened file descriptor * @param dest Destination opened file descriptor * @return 0 on succes, -1 on error */ int lr_copy_content(int source, int dest); /** If protocol is specified ("http://foo") return copy of path. * If path is absolute ("/foo/bar/") return path with "file://" prefix. * If path is relative ("bar/") return absolute path with "file://" prefix. * @param path * @return url with protocol */ char *lr_prepend_url_protocol(const char *path); /** Same as g_string_chunk_insert, but allows NULL as string. * If the string is NULL, then returns NULL and do nothing. * @param chunk String chunk * @param string String or NULL * @return a pointer to the copy of string within the chunk */ gchar * lr_string_chunk_insert(GStringChunk *chunk, const gchar *string); /** Warning callback to print warnings via GLib logger * For more info take a look at ::LrXmlParserWarningCb */ int lr_xml_parser_warning_logger(LrXmlParserWarningType type G_GNUC_UNUSED, char *msg, void *cbdata, GError **err G_GNUC_UNUSED) G_GNUC_UNUSED; /** From the GSList of pointers to LrMetalinkHash objects, select the * strongest one which librepo could calculate. * @param list List of LrMetalinkHash* * @param type Variable to store checksum type. * @param value Variable to store pointer to value (pointer to original * value from the list, NOT A COPY). * @return TRUE if usable checksum found, FALSE otherwise */ gboolean lr_best_checksum(GSList *list, LrChecksumType *type, gchar **value); /** Return malloced string with host part of url (protocol prefix + hostname) * @param url URL * @return Malloced url without path, just protocol and hostname */ gchar * lr_url_without_path(const char *url); /** Create a copy of NULL-terminated array of strings. * All strings in the copy are malloced - returned array * must be freed by g_strfreev() * @param array NULL-terminated array of strings or NULL * @return Copy of input array or NULL if input was NULL */ gchar ** lr_strv_dup(gchar **array); /** Check if string looks like a local path. * (This function doesn't do realpath resolving and existence checking) * If a path is empty, NULL or has protocol other then file:// specified * then the path is considered as remote, otherwise TRUE is returned. * @param path A string path * @return TRUE if path string looks like a local path */ gboolean lr_is_local_path(const gchar *path); /** Re-implementatio of g_key_file_save_to_file, * because the function is available since 2.40 but we need * to support older glib * @param key_file key file * @param filename filename * @param error GError ** * @return TRUE if successful, FALSE otherwise */ gboolean lr_key_file_save_to_file(GKeyFile *key_file, const gchar *filename, GError **error); #ifdef WITH_ZCHUNK /** Get LrChecksumType that corresponds to zck_hash * @param zck_checksum_type zck_hash value * @return checksum_type corresponding LrChecksumType value * * Return value will be LR_CHECKSUM_UNKNOWN if zck_checksum_type isn't available * as a LrChecksumType */ LrChecksumType lr_checksum_from_zck_hash(zck_hash zck_checksum_type); /** Get zck_hash that corresponds to LrChecksumType * @param checksum_type LrChecksumType value * @return zck_checksum_type corresponding zck_hash value * * Return value will be ZCK_HASH_UNKNOWN if checksum_type isn't available as a * zck_hash */ zck_hash lr_zck_hash_from_lr_checksum(LrChecksumType checksum_type); /** Base function for initializing zchunk file for reading while verifying * header checksum * @param checksum header checksum * @param checksum_type header checksum type * @param zck_header_size header size * @param fd file descriptor to read from * @return zck zchunk context opened for reading * * Return value will be NULL if the header checksum doesn't match the provided * checksum, is invalid, or if the header can't be read */ zckCtx * lr_zck_init_read_base(const char *checksum, LrChecksumType checksum_type, gint64 zck_header_size, int fd, GError **err); /** Base function for checking whether zchunk file has valid header checksum * @param checksum header checksum * @param checksum_type header checksum type * @param zck_header_size header size * @param fd file descriptor to read from * @return zck zchunk context opened for reading * * Return value will be FALSE if the header checksum doesn't match the provided * checksum, is invalid, or if the header can't be read */ gboolean lr_zck_valid_header_base(const char *checksum, LrChecksumType checksum_type, gint64 zck_header_size, int fd, GError **err); /** Initialize zchunk file for reading while verifying header checksum * @param checksum target containing checksum information * @param fd file descriptor to read from * @return zck zchunk context opened for reading * * Return value will be NULL if the header checksum doesn't match the provided * checksum, is invalid, or if the header can't be read */ zckCtx * lr_zck_init_read(LrDownloadTarget *target, char *filename, int fd, GError **err); /** Check whether zchunk file has valid header checksum * @param checksum target containing checksum information * @param fd file descriptor to read from * @return zck zchunk context opened for reading * * Return value will be FALSE if the header checksum doesn't match the provided * checksum, is invalid, or if the header can't be read */ gboolean lr_zck_valid_header(LrDownloadTarget *target, char *filename, int fd, GError **err); /** Recursively get list of all files in path that end with extension * @param path path to search * @param extension return files with this extension (including .) * @param err GError ** * @return filelist GSList* of files that have extension * * Return value will be NULL if no files match or there's an error. * err will be set if there's an error */ #endif /* WITH_ZCHUNK */ GSList * lr_get_recursive_files(char *path, char *extension, GError **err); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/version.h.in000066400000000000000000000036141372027637700171510ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_VERSION_H__ #define __LR_VERSION_H__ #include G_BEGIN_DECLS /** \defgroup version Library version constatnts and check macros * \addtogroup version * @{ */ #define LR_VERSION_MAJOR @LIBREPO_MAJOR@ /*!< Major Librepo version */ #define LR_VERSION_MINOR @LIBREPO_MINOR@ /*!< Minor Librepo version */ #define LR_VERSION_PATCH @LIBREPO_PATCH@ /*!< Patch Librepo version */ #define LR_VERSION "@LIBREPO_MAJOR@.@LIBREPO_MINOR@.@LIBREPO_PATCH@" /*!< Version string */ /** Macro for version check. * @param major Major version * @param minor Minor version * @param patch Patch version * @return True if current Librepo version is higher or equal */ #define LR_VERSION_CHECK(major,minor,patch) \ (LR_VERSION_MAJOR > (major) || \ (LR_VERSION_MAJOR == (major) && LR_VERSION_MINOR > (minor)) || \ (LR_VERSION_MAJOR == (major) && LR_VERSION_MINOR == (minor) && \ LR_VERSION_PATCH >= (patch))) /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/xmlparser.c000066400000000000000000000125641372027637700170730ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "xmlparser.h" #include "xmlparser_internal.h" #include "rcodes.h" LrParserData * lr_xml_parser_data_new(unsigned int numstates) { LrParserData *pd = g_new0(LrParserData, 1); pd->content = g_malloc(CONTENT_REALLOC_STEP); pd->acontent = CONTENT_REALLOC_STEP; pd->swtab = g_malloc0(sizeof(LrStatesSwitch *) * numstates); pd->sbtab = g_malloc(sizeof(unsigned int) * numstates); return pd; } void lr_xml_parser_data_free(LrParserData *pd) { if (!pd) return; g_free(pd->content); g_free(pd->swtab); g_free(pd->sbtab); g_free(pd); } void lr_char_handler(void *pdata, const xmlChar *s, int len) { int l; char *c; LrParserData *pd = pdata; if (pd->err) return; /* There was an error -> do nothing */ if (!pd->docontent) return; /* Do not store the content */ l = pd->lcontent + len + 1; if (l > pd->acontent) { pd->acontent = l + CONTENT_REALLOC_STEP; pd->content = realloc(pd->content, pd->acontent); } c = pd->content + pd->lcontent; pd->lcontent += len; while (len-- > 0) *c++ = *s++; *c = '\0'; } int lr_xml_parser_warning(LrParserData *pd, LrXmlParserWarningType type, const char *msg, ...) { int ret; va_list args; char *warn; GError *tmp_err; assert(pd); assert(msg); if (!pd->warningcb) return LR_CB_RET_OK; va_start(args, msg); g_vasprintf(&warn, msg, args); va_end(args); tmp_err = NULL; ret = pd->warningcb(type, warn, pd->warningcb_data, &tmp_err); g_free(warn); if (ret != LR_CB_RET_OK) { if (tmp_err) g_propagate_prefixed_error(&pd->err, tmp_err, "Parsing interrupted: "); else g_set_error(&pd->err, LR_XML_PARSER_ERROR, LRE_CBINTERRUPTED, "Parsing interrupted by user callback"); } assert(pd->err || ret == LR_CB_RET_OK); return ret; } gint64 lr_xml_parser_strtoll(LrParserData *pd, const char *nptr, unsigned int base) { gint64 val; char *endptr = NULL; assert(pd); assert(base <= 36 && base != 1); if (!nptr) return 0; val = g_ascii_strtoll(nptr, &endptr, base); if ((val == G_MAXINT64 || val == G_MININT64) && errno == ERANGE) lr_xml_parser_warning(pd, LR_XML_WARNING_BADATTRVAL, "Correct integer value \"%s\" caused overflow", nptr); else if (val == 0 && *endptr != '\0') lr_xml_parser_warning(pd, LR_XML_WARNING_BADATTRVAL, "Conversion of \"%s\" to integer failed", nptr); return val; } gboolean lr_xml_parser_generic(XmlParser parser, LrParserData *pd, int fd, GError **err) { /* Note: This function uses .err members of LrParserData! */ gboolean ret = TRUE; xmlParserCtxtPtr ctxt = xmlCreatePushParserCtxt(&parser, pd, NULL, 0, NULL); ctxt->linenumbers = 1; assert(ctxt); assert(pd); assert(fd >= 0); assert(!err || *err == NULL); while (1) { int len; char buf[XML_BUFFER_SIZE]; len = read(fd, (void *) buf, XML_BUFFER_SIZE); if (len < 0) { ret = FALSE; g_debug("%s: Error while reading xml : %s\n", __func__, g_strerror(errno)); g_set_error(err, LR_XML_PARSER_ERROR, LRE_IO, "Error while reading xml: %s", g_strerror(errno)); break; } if (xmlParseChunk(ctxt, buf, len, len == 0)) { xmlErrorPtr error = xmlCtxtGetLastError(ctxt); ret = FALSE; g_debug("%s: Parse error at line: %d (%s)", __func__, xmlSAX2GetLineNumber(ctxt), error->message); g_set_error(err, LR_XML_PARSER_ERROR, LRE_XMLPARSER, "Parse error at line: %d (%s)", xmlSAX2GetLineNumber(ctxt), error->message); break; } if (pd->err) { ret = FALSE; g_propagate_error(err, pd->err); break; } if (len == 0) break; } xmlFreeParserCtxt(ctxt); return ret; } librepo-1.12.1/librepo/xmlparser.h000066400000000000000000000047441372027637700171010ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_XMLPARSER_H__ #define __LR_XMLPARSER_H__ #include G_BEGIN_DECLS /** \defgroup xmlparser Common stuff for XML parsers in Librepo (datatypes, etc.) * \addtogroup xmlparser * @{ */ #define LR_CB_RET_OK 0 /*!< Return value for callbacks signalizing success */ #define LR_CB_RET_ERR 1 /*!< Return value for callbacks signalizing error */ /** Type of warnings reported by parsers by the warning callback. */ typedef enum { LR_XML_WARNING_UNKNOWNTAG, /*!< Unknown tag */ LR_XML_WARNING_MISSINGATTR, /*!< Missing attribute */ LR_XML_WARNING_UNKNOWNVAL, /*!< Unknown tag or attribute value */ LR_XML_WARNING_BADATTRVAL, /*!< Bad attribute value */ LR_XML_WARNING_MISSINGVAL, /*!< Missing tag value */ LR_XML_WARNING_SENTINEL, } LrXmlParserWarningType; /** Callback for XML parser warnings. All reported warnings are non-fatal, * and ignored by default. But if callback return LR_CB_RET_ERR instead of * LR_CB_RET_OK then parsing is immediately interrupted. * @param type Type of warning * @param msg Warning msg. The message is destroyed after the call. * If you want to use the message later, you have to copy it. * @param cbdata User data. * @param err GError ** * @return LR_CB_RET_OK (0) or LR_CB_RET_ERR (1) - stops the parsing */ typedef int (*LrXmlParserWarningCb)(LrXmlParserWarningType type, char *msg, void *cbdata, GError **err); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/xmlparser_internal.h000066400000000000000000000116771372027637700210000ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2013 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_XMLPARSER_INTERNAL_H__ #define __LR_XMLPARSER_INTERNAL_H__ #include #include #include "repomd.h" #include "metalink.h" G_BEGIN_DECLS /** \defgroup xmlparser_internal Common stuff for XML parsers in Librepo (datatypes, etc.) * \addtogroup xmlparser_internal * @{ */ #define XML_BUFFER_SIZE 8192 #define CONTENT_REALLOC_STEP 256 typedef xmlSAXHandler XmlParser; /** Structure used for elements in the state switches in XML parsers */ typedef struct { unsigned int from; /*!< State (current tag) */ char *ename; /*!< String name of sub-tag */ unsigned int to; /*!< State of sub-tag */ int docontent; /*!< Read text content of element? */ } LrStatesSwitch; /** Parser data */ typedef struct { int depth; /*!< Current depth in a XML tree */ int statedepth; /*!< Depth of the last known state (element) */ unsigned int state; /*!< current state */ GError *err; /*!< Error message */ /* Tag content related values */ int docontent; /*!< Store text content of the current element? */ char *content; /*!< Text content of the element */ int lcontent; /*!< The content length */ int acontent; /*!< Available bytes in the content */ XmlParser *parser; /*!< The parser */ LrStatesSwitch **swtab; /*!< Pointers to statesswitches table */ unsigned int *sbtab; /*!< stab[to_state] = from_state */ void *warningcb_data; /*!< User data fot he warningcb. */ LrXmlParserWarningCb warningcb; /*!< Warning callback */ /* Repomd related stuff */ gboolean repomdfound; /*!< True if the element was found */ LrYumRepoMd *repomd; /*!< Repomd object */ LrYumRepoMdRecord *repomdrecord; /*!< Repomd record object for a currently parsed element */ char *cpeid; /*!< cpeid value for the currently parsed distro tag */ /* Metalink related stuff */ char *filename; /*!< filename we are looking for in metalink */ int ignore; /*!< ignore all subelements of the current file element */ int found; /*!< wanted file was already parsed */ LrMetalink *metalink; /*!< metalink object */ LrMetalinkUrl *metalinkurl; /*!< Url in progress or NULL */ LrMetalinkHash *metalinkhash; /*!< Hash in progress or NULL */ LrMetalinkAlternate *metalinkalternate; /*!< Alternate in progress or NULL */ } LrParserData; /** Malloc and initialize common part of XML parser data. */ LrParserData * lr_xml_parser_data_new(unsigned int numstates); /** Frees XML parser data */ void lr_xml_parser_data_free(LrParserData *pd); /** XML character handler */ void XMLCALL lr_char_handler(void *pdata, const xmlChar *s, int len); /** Find attribute in list of attributes. * @param name Attribute name. * @param attr List of attributes of the tag * @return Value or NULL */ static inline const char * lr_find_attr(const char *name, const char **attr) { /* attr can be NULL when using libxml2 */ if (!attr) return NULL; while (*attr) { if (!strcmp(name, *attr)) return attr[1]; attr += 2; } return NULL; } /** Wrapper for user warning cb. * It checks if warningcb is defined, if defined, it build warning msg from * va_args, calls warningcb and propagate (set) error if necessary. */ int lr_xml_parser_warning(LrParserData *pd, LrXmlParserWarningType type, const char *msg, ...); /** strtoll with ability to call warning cb if error during conversion. */ gint64 lr_xml_parser_strtoll(LrParserData *pd, const char *nptr, unsigned int base); /** Generic parser. */ gboolean lr_xml_parser_generic(XmlParser parser, LrParserData *pd, int fd, GError **err); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/yum.c000066400000000000000000001316511372027637700156670ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _POSIX_SOURCE #define _DEFAULT_SOURCE #define BITS_IN_BYTE 8 #include #include #include #include #include #include #include #include #include #ifdef WITH_ZCHUNK #include #endif /* WITH_ZCHUNK */ #include "util.h" #include "metalink.h" #include "repomd.h" #include "downloader.h" #include "handle_internal.h" #include "result_internal.h" #include "yum_internal.h" #include "gpg.h" #include "cleanup.h" #include "librepo.h" /* helper functions for YumRepo manipulation */ LrYumRepo * lr_yum_repo_init(void) { return lr_malloc0(sizeof(LrYumRepo)); } void lr_yum_repo_free(LrYumRepo *repo) { if (!repo) return; for (GSList *elem = repo->paths; elem; elem = g_slist_next(elem)) { LrYumRepoPath *yumrepopath = elem->data; assert(yumrepopath); lr_free(yumrepopath->type); lr_free(yumrepopath->path); lr_free(yumrepopath); } g_slist_free(repo->paths); lr_free(repo->repomd); lr_free(repo->url); lr_free(repo->destdir); lr_free(repo->signature); lr_free(repo->mirrorlist); lr_free(repo->metalink); lr_free(repo); } static char * get_type(LrYumRepo *repo, const char *type) { if (!repo->use_zchunk) return g_strdup(type); gchar *chk_type = g_strconcat(type, "_zck", NULL); for (GSList *elem = repo->paths; elem; elem = g_slist_next(elem)) { LrYumRepoPath *yumrepopath = elem->data; assert(yumrepopath); if (!strcmp(yumrepopath->type, chk_type)) return chk_type; } g_free(chk_type); return g_strdup(type); } static const char * yum_repo_path(LrYumRepo *repo, const char *type) { assert(repo); for (GSList *elem = repo->paths; elem; elem = g_slist_next(elem)) { LrYumRepoPath *yumrepopath = elem->data; assert(yumrepopath); if (!strcmp(yumrepopath->type, type)) return yumrepopath->path; } return NULL; } const char * lr_yum_repo_path(LrYumRepo *repo, const char *type) { assert(repo); gchar *chk_type = get_type(repo, type); const char *path = yum_repo_path(repo, chk_type); g_free(chk_type); return path; } /** Append path to the repository object. * @param repo Yum repo object. * @param type Type of file. E.g. "primary", "filelists", ... * @param path Path to the file. */ static void lr_yum_repo_append(LrYumRepo *repo, const char *type, const char *path) { assert(repo); assert(type); assert(path); LrYumRepoPath *yumrepopath = lr_malloc(sizeof(LrYumRepoPath)); yumrepopath->type = g_strdup(type); yumrepopath->path = g_strdup(path); repo->paths = g_slist_append(repo->paths, yumrepopath); } static void lr_yum_repo_update(LrYumRepo *repo, const char *type, const char *path) { assert(repo); assert(type); assert(path); for (GSList *elem = repo->paths; elem; elem = g_slist_next(elem)) { LrYumRepoPath *yumrepopath = elem->data; assert(yumrepopath); if (!strcmp(yumrepopath->type, type)) { lr_free(yumrepopath->path); yumrepopath->path = g_strdup(path); return; } } lr_yum_repo_append(repo, type, path); } /* main business logic */ gint compare_records(gconstpointer a, gconstpointer b) { LrYumRepoMdRecord* yum_record = (LrYumRepoMdRecord*) a; char *type1 = (char *) yum_record->type; char *type2 = (char *) b; return g_strcmp0(type1, type2); } static void lr_yum_switch_to_zchunk(LrHandle *handle, LrYumRepoMd *repomd) { if (handle->yumdlist) { int x = 0; while (handle->yumdlist[x]) { char *check_type = g_strconcat(handle->yumdlist[x], "_zck", NULL); assert(check_type); /* Check whether we already want the zchunk version of this record */ int found = FALSE; int y = 0; while (handle->yumdlist[y]) { if (y == x) { y++; continue; } if (strcmp(handle->yumdlist[y], check_type) == 0) { found = TRUE; break; } y++; } if (found) { g_free(check_type); x++; continue; } found = FALSE; /* Check whether the zchunk version of this record exists */ for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) { LrYumRepoMdRecord *record = elem->data; if (strcmp(record->type, check_type) == 0) { g_debug("Found %s so using instead of %s", check_type, handle->yumdlist[x]); g_free(handle->yumdlist[x]); handle->yumdlist[x] = check_type; found = TRUE; break; } } if (!found) g_free(check_type); x++; } } return; } static gboolean lr_yum_repomd_record_enabled(LrHandle *handle, const char *type, GSList* records) { // Blacklist check if (handle->yumblist) { int x = 0; while (handle->yumblist[x]) { if (!strcmp(handle->yumblist[x], type)) return FALSE; x++; } } // Whitelist check if (handle->yumdlist) { int x = 0; while (handle->yumdlist[x]) { if (!strcmp(handle->yumdlist[x], type)) return TRUE; x++; } // Substitution check if (handle->yumslist) { for (GSList *elem = handle->yumslist; elem; elem = g_slist_next(elem)) { LrVar* subs = elem->data; if (!g_strcmp0(subs->val, type)) { char *orig = subs->var; for (guint i = 0; handle->yumdlist[i]; i++) { if (!g_strcmp0(orig, handle->yumdlist[i]) && !g_slist_find_custom(records, orig, (GCompareFunc) compare_records)) return TRUE; } return FALSE; } } } return FALSE; } return TRUE; } static CbData *cbdata_new(void *userdata, void *cbdata, LrProgressCb progresscb, LrHandleMirrorFailureCb hmfcb, const char *metadata) { CbData *data = calloc(1, sizeof(*data)); data->userdata = userdata; data->cbdata = cbdata; data->progresscb = progresscb; data->hmfcb = hmfcb; data->metadata = g_strdup(metadata); return data; } static void cbdata_free(CbData *data) { if (!data) return; free(data->metadata); free(data); } static int progresscb(void *clientp, double total_to_download, double downloaded) { CbData *data = clientp; if (data->progresscb) return data->progresscb(data->userdata, total_to_download, downloaded); return LR_CB_OK; } int hmfcb(void *clientp, const char *msg, const char *url) { CbData *data = clientp; if (data->hmfcb) return data->hmfcb(data->userdata, msg, url, data->metadata); return LR_CB_OK; } gboolean lr_prepare_repodata_dir(LrHandle *handle, GError **err) { int rc; int create_repodata_dir = 1; char *path_to_repodata; path_to_repodata = lr_pathconcat(handle->destdir, "repodata", NULL); if (handle->update) { /* Check if should create repodata/ subdir */ struct stat buf; if (stat(path_to_repodata, &buf) != -1) if (S_ISDIR(buf.st_mode)) create_repodata_dir = 0; } if (create_repodata_dir) { /* Prepare repodata/ subdir */ rc = mkdir(path_to_repodata, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH); if (rc == -1) { g_debug("%s: Cannot create dir: %s (%s)", __func__, path_to_repodata, g_strerror(errno)); g_set_error(err, LR_YUM_ERROR, LRE_CANNOTCREATEDIR, "Cannot create directory: %s: %s", path_to_repodata, g_strerror(errno)); lr_free(path_to_repodata); return FALSE; } } lr_free(path_to_repodata); return TRUE; } gboolean lr_store_mirrorlist_files(LrHandle *handle, LrYumRepo *repo, GError **err) { int fd; int rc; if (handle->mirrorlist_fd != -1) { char *ml_file_path = lr_pathconcat(handle->destdir, "mirrorlist", NULL); fd = open(ml_file_path, O_CREAT|O_TRUNC|O_RDWR, 0666); if (fd < 0) { g_debug("%s: Cannot create: %s", __func__, ml_file_path); g_set_error(err, LR_YUM_ERROR, LRE_IO, "Cannot create %s: %s", ml_file_path, g_strerror(errno)); lr_free(ml_file_path); return FALSE; } rc = lr_copy_content(handle->mirrorlist_fd, fd); close(fd); if (rc != 0) { g_debug("%s: Cannot copy content of mirrorlist file", __func__); g_set_error(err, LR_YUM_ERROR, LRE_IO, "Cannot copy content of mirrorlist file %s: %s", ml_file_path, g_strerror(errno)); lr_free(ml_file_path); return FALSE; } repo->mirrorlist = ml_file_path; } return TRUE; } gboolean lr_copy_metalink_content(LrHandle *handle, LrYumRepo *repo, GError **err) { int fd; int rc; if (handle->metalink_fd != -1) { char *ml_file_path = lr_pathconcat(handle->destdir, "metalink.xml", NULL); fd = open(ml_file_path, O_CREAT|O_TRUNC|O_RDWR, 0666); if (fd < 0) { g_debug("%s: Cannot create: %s", __func__, ml_file_path); g_set_error(err, LR_YUM_ERROR, LRE_IO, "Cannot create %s: %s", ml_file_path, g_strerror(errno)); lr_free(ml_file_path); return FALSE; } rc = lr_copy_content(handle->metalink_fd, fd); close(fd); if (rc != 0) { g_debug("%s: Cannot copy content of metalink file", __func__); g_set_error(err, LR_YUM_ERROR, LRE_IO, "Cannot copy content of metalink file %s: %s", ml_file_path, g_strerror(errno)); lr_free(ml_file_path); return FALSE; } repo->metalink = ml_file_path; } return TRUE; } int lr_prepare_repomd_xml_file(LrHandle *handle, char **path, GError **err) { int fd; *path = lr_pathconcat(handle->destdir, "/repodata/repomd.xml", NULL); fd = open(*path, O_CREAT|O_TRUNC|O_RDWR, 0666); if (fd == -1) { g_set_error(err, LR_YUM_ERROR, LRE_IO, "Cannot open %s: %s", *path, g_strerror(errno)); lr_free(*path); return -1; } return fd; } /** Check repomd.xml.asc if available. * Try to download and verify GPG signature (repomd.xml.asc). * Try to download only from the mirror where repomd.xml itself was * downloaded. It is because most of yum repositories are not signed * and try every mirror for signature is non effective. * Every mirror would be tried because mirrored_download function have * no clue if 404 for repomd.xml.asc means that no signature exists or * it is just error on the mirror and should try the next one. **/ gboolean lr_check_repomd_xml_asc_availability(LrHandle *handle, LrYumRepo *repo, int fd, char *path, GError **err) { GError *tmp_err = NULL; gboolean ret; if (handle->checks & LR_CHECK_GPG) { int fd_sig; char *url, *signature; signature = lr_pathconcat(handle->destdir, "repodata/repomd.xml.asc", NULL); fd_sig = open(signature, O_CREAT | O_TRUNC | O_RDWR, 0666); if (fd_sig == -1) { g_debug("%s: Cannot open: %s", __func__, signature); g_set_error(err, LR_YUM_ERROR, LRE_IO, "Cannot open %s: %s", signature, g_strerror(errno)); lr_free(signature); return FALSE; } url = lr_pathconcat(handle->used_mirror, "repodata/repomd.xml.asc", NULL); ret = lr_download_url(handle, url, fd_sig, &tmp_err); lr_free(url); close(fd_sig); if (!ret) { // Error downloading signature g_set_error(err, LR_YUM_ERROR, LRE_BADGPG, "GPG verification is enabled, but GPG signature " "is not available. This may be an error or the " "repository does not support GPG verification: %s", tmp_err->message); g_clear_error(&tmp_err); unlink(signature); lr_free(signature); return FALSE; } else { // Signature downloaded repo->signature = g_strdup(signature); ret = lr_gpg_check_signature(signature, path, handle->gnupghomedir, &tmp_err); lr_free(signature); if (!ret) { g_debug("%s: GPG signature verification failed: %s", __func__, tmp_err->message); g_propagate_prefixed_error(err, tmp_err, "repomd.xml GPG signature verification error: "); return FALSE; } g_debug("%s: GPG signature successfully verified", __func__); } } return TRUE; } void lr_get_best_checksum(const LrMetalink *metalink, GSList **checksums) { gboolean ret; LrChecksumType ch_type; gchar *ch_value; // From the metalink itself ret = lr_best_checksum(metalink->hashes, &ch_type, &ch_value); if (ret) { LrDownloadTargetChecksum *dtch; dtch = lr_downloadtargetchecksum_new(ch_type, ch_value); *checksums = g_slist_prepend(*checksums, dtch); g_debug("%s: Expected checksum for repomd.xml: (%s) %s", __func__, lr_checksum_type_to_str(ch_type), ch_value); } // From the alternates entries for (GSList *elem = metalink->alternates; elem; elem = g_slist_next(elem)) { LrMetalinkAlternate *alt = elem->data; ret = lr_best_checksum(alt->hashes, &ch_type, &ch_value); if (ret) { LrDownloadTargetChecksum *dtch; dtch = lr_downloadtargetchecksum_new(ch_type, ch_value); *checksums = g_slist_prepend(*checksums, dtch); g_debug("%s: Expected alternate checksum for repomd.xml: (%s) %s", __func__, lr_checksum_type_to_str(ch_type), ch_value); } } } CbData * lr_get_metadata_failure_callback(const LrHandle *handle) { CbData *cbdata = NULL; if (handle->hmfcb) { cbdata = cbdata_new(handle->user_data, NULL, NULL, handle->hmfcb, "repomd.xml"); } return cbdata; } gboolean lr_yum_download_url(LrHandle *lr_handle, const char *url, int fd, gboolean no_cache, gboolean is_zchunk, GError **err) { gboolean ret; LrDownloadTarget *target; GError *tmp_err = NULL; assert(url); assert(!err || *err == NULL); CbData *cbdata = cbdata_new(lr_handle->user_data, NULL, lr_handle->user_cb, lr_handle->hmfcb, url); // Prepare target target = lr_downloadtarget_new(lr_handle, url, NULL, fd, NULL, NULL, 0, 0,(lr_handle->user_cb) ? progresscb : NULL, cbdata, NULL, (lr_handle->hmfcb) ? hmfcb : NULL, NULL, 0, 0, NULL, no_cache, is_zchunk); // Download the target ret = lr_download_target(target, &tmp_err); assert(ret || tmp_err); assert(!(target->err) || !ret); cbdata_free(cbdata); if (!ret) g_propagate_error(err, tmp_err); lr_downloadtarget_free(target); lseek(fd, 0, SEEK_SET); return ret; } static gboolean lr_yum_download_repomd(LrHandle *handle, LrMetalink *metalink, int fd, GError **err) { int ret = TRUE; GError *tmp_err = NULL; assert(!err || *err == NULL); g_debug("%s: Downloading repomd.xml via mirrorlist", __func__); GSList *checksums = NULL; if (metalink && (handle->checks & LR_CHECK_CHECKSUM)) { lr_get_best_checksum(metalink, &checksums); } CbData *cbdata = cbdata_new(handle->user_data, NULL, handle->user_cb, handle->hmfcb, "repomd.xml"); LrDownloadTarget *target = lr_downloadtarget_new(handle, "repodata/repomd.xml", NULL, fd, NULL, checksums, 0, 0, (handle->user_cb) ? progresscb : NULL, cbdata, NULL, (handle->hmfcb) ? hmfcb : NULL, NULL, 0, 0, NULL, TRUE, FALSE); ret = lr_download_target(target, &tmp_err); assert((ret && !tmp_err) || (!ret && tmp_err)); if (cbdata) cbdata_free(cbdata); if (tmp_err) { g_propagate_prefixed_error(err, tmp_err, "Cannot download repomd.xml: "); } else if (target->err) { assert(0); // This should not happen since failfast should be TRUE ret = FALSE; g_set_error(err, LR_DOWNLOADER_ERROR, target->rcode, "Cannot download repomd.xml: %s",target->err); } else { // Set mirror used for download a repomd.xml to the handle // TODO: Get rid of use_mirror attr lr_free(handle->used_mirror); handle->used_mirror = g_strdup(target->usedmirror); } lr_downloadtarget_free(target); if (!ret) { /* Download of repomd.xml was not successful */ g_debug("%s: repomd.xml download was unsuccessful", __func__); } return ret; } gboolean prepare_repo_download_std_target(LrHandle *handle, LrYumRepoMdRecord *record, char **path, int *fd, GSList **checksums, GSList **targets, GError **err) { *path = lr_pathconcat(handle->destdir, record->location_href, NULL); *fd = open(*path, O_CREAT|O_TRUNC|O_RDWR, 0666); if (*fd < 0) { g_debug("%s: Cannot create/open %s (%s)", __func__, *path, g_strerror(errno)); g_set_error(err, LR_YUM_ERROR, LRE_IO, "Cannot create/open %s: %s", *path, g_strerror(errno)); lr_free(*path); g_slist_free_full(*targets, (GDestroyNotify) lr_downloadtarget_free); return FALSE; } if (handle->checks & LR_CHECK_CHECKSUM) { // Select proper checksum type only if checksum check is enabled LrDownloadTargetChecksum *checksum; checksum = lr_downloadtargetchecksum_new( lr_checksum_type(record->checksum_type), record->checksum); *checksums = g_slist_prepend(*checksums, checksum); } return TRUE; } #ifdef WITH_ZCHUNK gboolean prepare_repo_download_zck_target(LrHandle *handle, LrYumRepoMdRecord *record, char **path, int *fd, GSList **checksums, GSList **targets, GError **err) { *path = lr_pathconcat(handle->destdir, record->location_href, NULL); *fd = open(*path, O_CREAT|O_RDWR, 0666); if (*fd < 0) { g_debug("%s: Cannot create/open %s (%s)", __func__, *path, g_strerror(errno)); g_set_error(err, LR_YUM_ERROR, LRE_IO, "Cannot create/open %s: %s", *path, g_strerror(errno)); lr_free(*path); g_slist_free_full(*targets, (GDestroyNotify) lr_downloadtarget_free); return FALSE; } if (handle->checks & LR_CHECK_CHECKSUM) { // Select proper checksum type only if checksum check is enabled LrDownloadTargetChecksum *checksum; checksum = lr_downloadtargetchecksum_new( lr_checksum_type(record->header_checksum_type), record->header_checksum); *checksums = g_slist_prepend(*checksums, checksum); } return TRUE; } #endif /* WITH_ZCHUNK */ gboolean prepare_repo_download_targets(LrHandle *handle, LrYumRepo *repo, LrYumRepoMd *repomd, LrMetadataTarget *mdtarget, GSList **targets, GSList **cbdata_list, GError **err) { char *destdir; /* Destination dir */ destdir = handle->destdir; assert(destdir); assert(strlen(destdir)); assert(!err || *err == NULL); if(handle->cachedir) { lr_yum_switch_to_zchunk(handle, repomd); repo->use_zchunk = TRUE; } else { g_debug("%s: Cache directory not set, disabling zchunk", __func__); repo->use_zchunk = FALSE; } for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) { int fd; char *path; LrDownloadTarget *target; LrYumRepoMdRecord *record = elem->data; CbData *cbdata = NULL; void *user_cbdata = NULL; LrEndCb endcb = NULL; if (mdtarget != NULL) { user_cbdata = mdtarget->cbdata; endcb = mdtarget->endcb; } assert(record); if (!lr_yum_repomd_record_enabled(handle, record->type, repomd->records)) continue; char *location_href = record->location_href; char *dest_dir = realpath(handle->destdir, NULL); path = lr_pathconcat(handle->destdir, record->location_href, NULL); char *requested_dir = realpath(dirname(path), NULL); lr_free(path); if (!g_str_has_prefix(requested_dir, dest_dir)) { g_debug("%s: Invalid path: %s", __func__, location_href); g_set_error(err, LR_YUM_ERROR, LRE_IO, "Invalid path: %s", location_href); g_slist_free_full(*targets, (GDestroyNotify) lr_downloadtarget_free); free(requested_dir); free(dest_dir); return FALSE; } free(requested_dir); free(dest_dir); gboolean is_zchunk = FALSE; #ifdef WITH_ZCHUNK if (handle->cachedir && record->header_checksum) is_zchunk = TRUE; #endif /* WITH_ZCHUNK */ GSList *checksums = NULL; if (is_zchunk) { #ifdef WITH_ZCHUNK if(!prepare_repo_download_zck_target(handle, record, &path, &fd, &checksums, targets, err)) return FALSE; #endif /* WITH_ZCHUNK */ } else { if(!prepare_repo_download_std_target(handle, record, &path, &fd, &checksums, targets, err)) return FALSE; } if (handle->user_cb || handle->hmfcb) { cbdata = cbdata_new(handle->user_data, user_cbdata, handle->user_cb, handle->hmfcb, record->type); *cbdata_list = g_slist_append(*cbdata_list, cbdata); } target = lr_downloadtarget_new(handle, location_href, record->location_base, fd, NULL, checksums, 0, 0, NULL, cbdata, endcb, NULL, NULL, 0, 0, NULL, FALSE, is_zchunk); if(is_zchunk) { #ifdef WITH_ZCHUNK target->expectedsize = record->size_header; target->zck_header_size = record->size_header; #endif /* WITH_ZCHUNK */ } if (mdtarget != NULL) mdtarget->repomd_records_to_download++; *targets = g_slist_append(*targets, target); /* Because path may already exists in repo (while update) */ lr_yum_repo_update(repo, record->type, path); lr_free(path); } return TRUE; } gboolean error_handling(GSList *targets, GError **dest_error, GError *src_error) { if (src_error) { g_propagate_prefixed_error(dest_error, src_error, "Downloading error: "); return FALSE; } else { int code = LRE_OK; char *error_summary = NULL; for (GSList *elem = targets; elem; elem = g_slist_next(elem)) { LrDownloadTarget *target = elem->data; if (target->rcode != LRE_OK) { if (code == LRE_OK) { // First failed download target found code = target->rcode; error_summary = g_strconcat(target->path, " - ", target->err, NULL); } else { error_summary = g_strconcat(error_summary, "; ", target->path, " - ", target->err, NULL); } } close(target->fd); } if (code != LRE_OK) { // At least one target failed g_set_error(dest_error, LR_DOWNLOADER_ERROR, code, "Downloading error(s): %s", error_summary); g_free(error_summary); return FALSE; } } return TRUE; } gboolean lr_yum_download_repos(GSList *targets, GError **err) { gboolean ret; GSList *download_targets = NULL; GSList *cbdata_list = NULL; GError *download_error = NULL; for (GSList *elem = targets; elem; elem = g_slist_next(elem)) { LrMetadataTarget *target = elem->data; if (!target->handle) { continue; } prepare_repo_download_targets(target->handle, target->repo, target->repomd, target, &download_targets, &cbdata_list, &download_error); } if (!download_targets) { g_propagate_error(err, download_error); return TRUE; } ret = lr_download_single_cb(download_targets, FALSE, (cbdata_list) ? progresscb : NULL, (cbdata_list) ? hmfcb : NULL, &download_error); error_handling(download_targets, err, download_error); g_slist_free_full(cbdata_list, (GDestroyNotify)cbdata_free); g_slist_free_full(download_targets, (GDestroyNotify)lr_downloadtarget_free); return ret; } gboolean lr_yum_download_repo(LrHandle *handle, LrYumRepo *repo, LrYumRepoMd *repomd, GError **err) { gboolean ret = TRUE; GSList *targets = NULL; GSList *cbdata_list = NULL; GError *tmp_err = NULL; assert(!err || *err == NULL); prepare_repo_download_targets(handle, repo, repomd, NULL, &targets, &cbdata_list, err); if (!targets) return TRUE; ret = lr_download_single_cb(targets, FALSE, (cbdata_list) ? progresscb : NULL, (cbdata_list) ? hmfcb : NULL, &tmp_err); assert((ret && !tmp_err) || (!ret && tmp_err)); ret = error_handling(targets, err, tmp_err); g_slist_free_full(cbdata_list, (GDestroyNotify)cbdata_free); g_slist_free_full(targets, (GDestroyNotify)lr_downloadtarget_free); return ret; } static gboolean lr_yum_check_checksum_of_md_record(LrYumRepoMdRecord *rec, const char *path, GError **err) { int fd; char *expected_checksum; LrChecksumType checksum_type; gboolean ret, matches; gboolean is_zchunk = FALSE; GError *tmp_err = NULL; assert(!err || *err == NULL); if (!rec || !path) return TRUE; #ifdef WITH_ZCHUNK if(rec->header_checksum) { expected_checksum = rec->header_checksum; checksum_type = lr_checksum_type(rec->header_checksum_type); is_zchunk = TRUE; } else { #endif /* WITH_ZCHUNK */ expected_checksum = rec->checksum; checksum_type = lr_checksum_type(rec->checksum_type); #ifdef WITH_ZCHUNK } #endif /* WITH_ZCHUNK */ g_debug("%s: Checking checksum of %s (expected: %s [%s])", __func__, path, expected_checksum, rec->checksum_type); if (!expected_checksum) { // Empty checksum - suppose it's ok g_debug("%s: No checksum in repomd", __func__); return TRUE; } if (checksum_type == LR_CHECKSUM_UNKNOWN) { g_debug("%s: Unknown checksum", __func__); g_set_error(err, LR_YUM_ERROR, LRE_UNKNOWNCHECKSUM, "Unknown checksum type for %s", path); return FALSE; } fd = open(path, O_RDONLY); if (fd < 0) { g_debug("%s: Cannot open %s", __func__, path); g_set_error(err, LR_YUM_ERROR, LRE_IO, "Cannot open %s: %s", path, g_strerror(errno)); return FALSE; } if (is_zchunk) { #ifdef WITH_ZCHUNK ret = FALSE; matches = FALSE; zckCtx *zck = lr_zck_init_read_base(expected_checksum, checksum_type, rec->size_header, fd, &tmp_err); if (!tmp_err) { if(zck_validate_checksums(zck) < 1) { g_set_error(&tmp_err, LR_YUM_ERROR, LRE_ZCK, "Unable to validate zchunk checksums"); } else { ret = TRUE; matches = TRUE; } } if (zck) zck_free(&zck); #endif /* WITH_ZCHUNK */ } else { ret = lr_checksum_fd_cmp(checksum_type, fd, expected_checksum, 1, &matches, &tmp_err); } close(fd); assert(ret || tmp_err); if (!ret) { // Checksum calculation error g_debug("%s: Checksum check %s - Error: %s", __func__, path, tmp_err->message); g_propagate_prefixed_error(err, tmp_err, "Checksum error %s: ", path); return FALSE; } else if (!matches) { g_debug("%s: Checksum check %s - Mismatch", __func__, path); g_set_error(err, LR_YUM_ERROR, LRE_BADCHECKSUM, "Checksum mismatch %s", path); return FALSE; } g_debug("%s: Checksum check - Passed", __func__); return TRUE; } static gboolean lr_yum_check_repo_checksums(LrYumRepo *repo, LrYumRepoMd *repomd, GError **err) { assert(!err || *err == NULL); for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) { gboolean ret; LrYumRepoMdRecord *record = elem->data; assert(record); const char *path = yum_repo_path(repo, record->type); ret = lr_yum_check_checksum_of_md_record(record, path, err); if (!ret) return FALSE; } return TRUE; } static gboolean lr_yum_use_local_load_base(LrHandle *handle, LrResult *result, LrYumRepo *repo, LrYumRepoMd *repomd, const gchar *baseurl, GError **err) { gboolean ret; GError *tmp_err = NULL; _cleanup_free_ gchar *path = NULL; _cleanup_free_ gchar *sig = NULL; _cleanup_fd_close_ int fd = -1; if (handle->mirrorlist_fd != -1) { // Locate mirrorlist if available. gchar *mrl_fn = lr_pathconcat(baseurl, "mirrorlist", NULL); if (g_file_test(mrl_fn, G_FILE_TEST_IS_REGULAR)) { g_debug("%s: Found local mirrorlist: %s", __func__, mrl_fn); repo->mirrorlist = mrl_fn; } else { repo->mirrorlist = NULL; lr_free(mrl_fn); } } if (handle->metalink_fd != -1) { // Locate metalink.xml if available. gchar *mtl_fn = lr_pathconcat(baseurl, "metalink.xml", NULL); if (g_file_test(mtl_fn, G_FILE_TEST_IS_REGULAR)) { g_debug("%s: Found local metalink: %s", __func__, mtl_fn); repo->metalink = mtl_fn; } else { repo->metalink = NULL; lr_free(mtl_fn); } } // Open repomd.xml path = lr_pathconcat(baseurl, "repodata/repomd.xml", NULL); fd = open(path, O_RDONLY); if (fd < 0) { g_debug("%s: open(%s): %s", __func__, path, g_strerror(errno)); g_set_error(err, LR_YUM_ERROR, LRE_IO, "Cannot open %s: %s", path, g_strerror(errno)); return FALSE; } // Parse repomd.xml g_debug("%s: Parsing repomd.xml", __func__); ret = lr_yum_repomd_parse_file(repomd, fd, lr_xml_parser_warning_logger, "Repomd xml parser", &tmp_err); if (!ret) { g_debug("%s: Parsing unsuccessful: %s", __func__, tmp_err->message); g_propagate_prefixed_error(err, tmp_err, "repomd.xml parser error: "); return FALSE; } // Fill result object result->destdir = g_strdup(baseurl); repo->destdir = g_strdup(baseurl); repo->repomd = g_strdup(path); // Check if signature file exists sig = lr_pathconcat(baseurl, "repodata/repomd.xml.asc", NULL); if (access(sig, F_OK) == 0) repo->signature = g_strdup(sig); // Signature checking if (handle->checks & LR_CHECK_GPG) { if (!repo->signature) { // Signature doesn't exist g_set_error(err, LR_YUM_ERROR, LRE_BADGPG, "GPG verification is enabled, but GPG signature " "repomd.xml.asc is not available. This may be an " "error or the repository does not support GPG verification."); return FALSE; } ret = lr_gpg_check_signature(repo->signature, repo->repomd, handle->gnupghomedir, &tmp_err); if (!ret) { g_debug("%s: repomd.xml GPG signature verification failed: %s", __func__, tmp_err->message); g_propagate_prefixed_error(err, tmp_err, "repomd.xml GPG signature verification failed: "); return FALSE; } } // Done - repomd is loaded and checked g_debug("%s: Repomd revision: %s", __func__, repomd->revision); return TRUE; } /* Do not duplicate repoata, just locate the local one */ static gboolean lr_yum_use_local(LrHandle *handle, LrResult *result, GError **err) { char *baseurl; LrYumRepo *repo; LrYumRepoMd *repomd; assert(!err || *err == NULL); g_debug("%s: Locating repo..", __func__); // Shortcuts repo = result->yum_repo; repomd = result->yum_repomd; baseurl = handle->urls[0]; // Skip "file://" prefix if present if (g_str_has_prefix(baseurl, "file://")) baseurl += 7; else if (g_str_has_prefix(baseurl, "file:")) baseurl += 5; // Check sanity if (strstr(baseurl, "://")) { g_set_error(err, LR_YUM_ERROR, LRE_NOTLOCAL, "URL: %s doesn't seem to be a local repository", baseurl); return FALSE; } if (!handle->update) { // Load repomd.xml and mirrorlist+metalink if locally available if (!lr_yum_use_local_load_base(handle, result, repo, repomd, baseurl, err)) return FALSE; } if(handle->cachedir) { lr_yum_switch_to_zchunk(handle, repomd); repo->use_zchunk = TRUE; } else { g_debug("%s: Cache directory not set, disabling zchunk", __func__); repo->use_zchunk = FALSE; } // Locate rest of metadata files for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) { _cleanup_free_ char *path = NULL; LrYumRepoMdRecord *record = elem->data; assert(record); if (!lr_yum_repomd_record_enabled(handle, record->type, repomd->records)) continue; // Caller isn't interested in this record type if (yum_repo_path(repo, record->type)) continue; // This path already exists in repo path = lr_pathconcat(baseurl, record->location_href, NULL); if (access(path, F_OK) == -1) { // A repo file is missing if (!handle->ignoremissing) { g_debug("%s: Incomplete repository - %s is missing", __func__, path); g_set_error(err, LR_YUM_ERROR, LRE_INCOMPLETEREPO, "Incomplete repository - %s is missing", path); return FALSE; } continue; } lr_yum_repo_append(repo, record->type, path); } g_debug("%s: Repository was successfully located", __func__); return TRUE; } static gboolean lr_yum_download_remote(LrHandle *handle, LrResult *result, GError **err) { gboolean ret = TRUE; int fd; LrYumRepo *repo; LrYumRepoMd *repomd; GError *tmp_err = NULL; assert(!err || *err == NULL); repo = result->yum_repo; repomd = result->yum_repomd; g_debug("%s: Downloading/Copying repo..", __func__); if (!lr_prepare_repodata_dir(handle, err)) return FALSE; if (!handle->update) { char *path = NULL; if (!lr_store_mirrorlist_files(handle, repo, err)) return FALSE; if (!lr_copy_metalink_content(handle, repo, err)) return FALSE; if ((fd = lr_prepare_repomd_xml_file(handle, &path, err)) == -1) return FALSE; /* Download repomd.xml */ ret = lr_yum_download_repomd(handle, handle->metalink, fd, err); if (!ret) { close(fd); lr_free(path); return FALSE; } if (!lr_check_repomd_xml_asc_availability(handle, repo, fd, path, err)) { close(fd); lr_free(path); return FALSE; } lseek(fd, 0, SEEK_SET); /* Parse repomd */ g_debug("%s: Parsing repomd.xml", __func__); ret = lr_yum_repomd_parse_file(repomd, fd, lr_xml_parser_warning_logger, "Repomd xml parser", &tmp_err); close(fd); if (!ret) { g_debug("%s: Parsing unsuccessful: %s", __func__, tmp_err->message); g_propagate_prefixed_error(err, tmp_err, "repomd.xml parser error: "); lr_free(path); return FALSE; } /* Fill result object */ result->destdir = g_strdup(handle->destdir); repo->destdir = g_strdup(handle->destdir); repo->repomd = path; if (handle->used_mirror) repo->url = g_strdup(handle->used_mirror); else repo->url = g_strdup(handle->urls[0]); g_debug("%s: Repomd revision: %s", repomd->revision, __func__); } /* Download rest of metadata files */ ret = lr_yum_download_repo(handle, repo, repomd, &tmp_err); assert((ret && !tmp_err) || (!ret && tmp_err)); if (!ret) { g_debug("%s: Repository download error: %s", __func__, tmp_err->message); g_propagate_prefixed_error(err, tmp_err, "Yum repo downloading error: "); return FALSE; } return TRUE; } gboolean lr_yum_perform(LrHandle *handle, LrResult *result, GError **err) { int ret = TRUE; LrYumRepo *repo; LrYumRepoMd *repomd; assert(handle); assert(!err || *err == NULL); if (!result) { g_set_error(err, LR_YUM_ERROR, LRE_BADFUNCARG, "Missing result parameter"); return FALSE; } if (!handle->urls && !handle->mirrorlisturl && !handle->metalinkurl) { g_set_error(err, LR_YUM_ERROR, LRE_NOURL, "No LRO_URLS, LRO_MIRRORLISTURL nor LRO_METALINKURL specified"); return FALSE; } if (handle->local && (!handle->urls || !handle->urls[0])) { g_set_error(err, LR_YUM_ERROR, LRE_NOURL, "Localrepo specified, but no LRO_URLS set"); return FALSE; } if (handle->update) { // Download/Locate only specified files if (!result->yum_repo || !result->yum_repomd) { g_set_error(err, LR_YUM_ERROR, LRE_INCOMPLETERESULT, "Incomplete result object - " "Cannot update on this result object"); return FALSE; } } else { // Download/Locate from scratch if (result->yum_repo || result->yum_repomd) { g_set_error(err, LR_YUM_ERROR, LRE_ALREADYUSEDRESULT, "This result object is not clear - " "Already used result object"); return FALSE; } result->yum_repo = lr_yum_repo_init(); result->yum_repomd = lr_yum_repomd_init(); } repo = result->yum_repo; repomd = result->yum_repomd; if (handle->local) { // Do not duplicate repository, just use the existing local one ret = lr_yum_use_local(handle, result, err); if (!ret) return FALSE; if (handle->checks & LR_CHECK_CHECKSUM) ret = lr_yum_check_repo_checksums(repo, repomd, err); } else { // Download remote/Duplicate local repository // Note: All checksums are checked while downloading ret = lr_yum_download_remote(handle, result, err); } return ret; } librepo-1.12.1/librepo/yum.h000066400000000000000000000121031372027637700156620ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_YUMREPO_H__ #define __LR_YUMREPO_H__ #include #include "handle.h" #include "metalink.h" #include "rcodes.h" #include "repomd.h" #include "result.h" G_BEGIN_DECLS /** \defgroup yum Yum repo manipulation * \addtogroup yum * @{ */ /** Path to single metadata file from repomd.xml. */ typedef struct { char *type; /*!< Type of record (e.g. "primary") */ char *path; /*!< Path to the file (e.g. foo/bar/repodata/primary.xml) */ } LrYumRepoPath; /** Yum repository */ typedef struct { GSList *paths; /*!< Paths to repo files. List of ::LrYumRepoPath*s */ char *repomd; /*!< Path to repomd.xml */ char *url; /*!< URL from where repo was downloaded */ char *destdir; /*!< Local path to the repo */ char *signature; /*!< Path to signature if available and signature was downloaded (GPG check was enabled during repo downloading) */ char *mirrorlist; /*!< Mirrolist filename */ char *metalink; /*!< Metalink filename */ gboolean use_zchunk; /*!< Use zchunk in this repo */ } LrYumRepo; /** Mirror Failure Callback Data */ typedef struct CbData_s { void *userdata; /*!< User data */ void *cbdata; /*!< User's callback data */ LrProgressCb progresscb; /*!< Progress callback */ LrHandleMirrorFailureCb hmfcb; /*!< Handle mirror failure callback */ char *metadata; /*!< "primary", "filelists", ... */ } CbData; /** Allocate new yum repo object. * @return New yum repo object. */ LrYumRepo * lr_yum_repo_init(void); /** Free yum repo - free its item and the repo itself. * @param repo Yum repo object. */ void lr_yum_repo_free(LrYumRepo *repo); /** Retruns path for the file from repository. * @param repo Yum repo object. * @param type Type of path. E.g. "primary", "filelists", ... * @return Path or NULL. */ const char * lr_yum_repo_path(LrYumRepo *repo, const char *type); /** * Handle mirror failure callback * @param clientp Pointer to user data. * @param msg Error message. * @param url Mirror URL. * @return See LrCbReturnCode codes */ int hmfcb(void *clientp, const char *msg, const char *url); /** Prepares directory for repo data * @param handle Handle object containing path to repo data * @param err Object for storing errors * @return True on success */ gboolean lr_prepare_repodata_dir(LrHandle *handle, GError **err); /** Stores mirror list files * @param handle Handle object containing path to mirror list * @param repo Yum repository * @param err Object for storing errors * @return True on success */ gboolean lr_store_mirrorlist_files(LrHandle *handle, LrYumRepo *repo, GError **err); /** Copies metalink content * @param handle Handle object containing dest dir path * @param repo Yum repository * @param err Object for storing errors * @return True on success */ gboolean lr_copy_metalink_content(LrHandle *handle, LrYumRepo *repo, GError **err); /** Prepares repomd.xml file * @param handle Handle object containing dest dir path * @param path * @param err Object for storing errors * @return File descriptor of repomd.xml file */ int lr_prepare_repomd_xml_file(LrHandle *handle, char **path, GError **err); gboolean lr_check_repomd_xml_asc_availability(LrHandle *handle, LrYumRepo *repo, int fd, char *path, GError **err); /** Stores best checksum on the beginning of @param checksums * @param metalink Metalink * @param checksums List of checksums */ void lr_get_best_checksum(const LrMetalink *metalink, GSList **checksums); /** Returns mirror failure callback data * @param handle Handle object * @return Mirror Failure Callback Data */ CbData * lr_get_metadata_failure_callback(const LrHandle *handle); /** * * @param targets * @param err * @return return TRUE on success, FALSE otherwise */ gboolean lr_yum_download_repos(GSList *targets, GError **err); /** @} */ G_END_DECLS #endif librepo-1.12.1/librepo/yum_internal.h000066400000000000000000000024641372027637700175670ustar00rootroot00000000000000/* librepo - A library providing (libcURL like) API to downloading repository * Copyright (C) 2012 Tomas Mlcoch * * Licensed under the GNU Lesser General Public License Version 2.1 * * 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 2.1 of the License, or (at your option) any later version. * * This library 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LR_YUM_INTERNAL_H__ #define __LR_YUM_INTERNAL_H__ #include #include "rcodes.h" #include "result.h" #include "handle.h" G_BEGIN_DECLS gboolean lr_yum_perform(LrHandle *handle, LrResult *result, GError **err); gboolean lr_yum_download_url(LrHandle *lr_handle, const char *url, int fd, gboolean no_cache, gboolean is_zchunk, GError **err); G_END_DECLS #endif librepo-1.12.1/still_reachable.supp000066400000000000000000000752051372027637700173050ustar00rootroot00000000000000{ Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:checksum_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:checksum_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:curltargetlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:curltargetlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:gpg_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:gpg_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:handle_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:handle_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:internal_mirrorlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:internal_mirrorlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:metalink_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:metalink_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:mirrorlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:mirrorlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:repomd_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:repomd_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:util_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:util_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_create fun:main } { Memcheck:Leak fun:malloc fun:strdup fun:set_selinuxmnt fun:verify_selinuxmnt fun:init_lib fun:call_init.part.0 fun:_dl_init obj:/usr/lib64/ld-2.15.so obj:* obj:* obj:* } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:checksum_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:curltargetlist_suite fun:main } { Memcheck:Leak fun:realloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:curltargetlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:gpg_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:handle_suite fun:main } { Memcheck:Leak fun:realloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:handle_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:internal_mirrorlist_suite fun:main } { Memcheck:Leak fun:realloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:internal_mirrorlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:metalink_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:mirrorlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:repomd_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:suite_create fun:util_suite fun:main } { Memcheck:Leak fun:malloc fun:strdup fun:lr_strdup fun:init_test_globals fun:main } { Memcheck:Leak fun:malloc fun:lr_malloc fun:lr_pathconcat fun:init_test_globals fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_create fun:main } { Memcheck:Leak fun:malloc fun:strdup fun:lr_strdup fun:lr_gettmpdir fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:_tcase_add_test fun:checksum_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:_tcase_add_test fun:curltargetlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:_tcase_add_test fun:gpg_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:_tcase_add_test fun:handle_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:_tcase_add_test fun:internal_mirrorlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:_tcase_add_test fun:metalink_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:_tcase_add_test fun:mirrorlist_suite fun:main } { Memcheck:Leak fun:realloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:mirrorlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:_tcase_add_test fun:repomd_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:_tcase_add_test fun:util_suite fun:main } { Memcheck:Leak fun:realloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:lr_malloc fun:lr_pathconcat fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:checksum_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:curltargetlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:gpg_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:handle_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:internal_mirrorlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:metalink_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:mirrorlist_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:repomd_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:util_suite fun:main } { Memcheck:Leak fun:realloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:metalink_suite fun:main } { Memcheck:Leak fun:realloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:util_suite fun:main } { Memcheck:Leak fun:realloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:fdopen@@GLIBC_2.2.5 fun:tmpfile@@GLIBC_2.2.5 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_create fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:_tcase_add_test fun:util_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:_tcase_add_test fun:util_suite fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libcheck.so.0.0.0 fun:tcase_create fun:curltargetlist_suite fun:main } { Memcheck:Leak fun:malloc fun:strdup obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_new fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:strdup obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_ctx_set_engine_info fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_new fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:gpgme_new fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_import fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_ctx_set_engine_info fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:strdup obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_import fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_import fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_import fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_import fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_import fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:strdup obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_import fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_import fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_import fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_data_new_from_fd fun:lr_gpg_import_key fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:strdup obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_new fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:strdup obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_ctx_set_engine_info fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_new fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:gpgme_new fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_verify fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_ctx_set_engine_info fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:strdup obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_verify fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all } { Memcheck:Leak fun:calloc obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_verify fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_verify fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_verify fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all } { Memcheck:Leak fun:calloc obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_verify fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:strdup obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_verify fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_verify fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_op_verify fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc obj:/usr/lib64/libgpgme.so.11.7.0 fun:gpgme_data_new_from_fd fun:lr_gpg_check_signature_fd fun:lr_gpg_check_signature fun:test_gpg_check_signature obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:strdup fun:PR_NewLogModule fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:PR_CreateStack fun:_PR_InitFdCache fun:_PR_InitIO fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:_PR_InitLinker fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewLogModule fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:_PR_InitMW fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:PR_ErrorInstallTable fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:_dlerror_run fun:dlopen@@GLIBC_2.2.5 fun:_PR_InitZones fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:_PR_Getfd fun:pt_SetMethods.isra.11 fun:_PR_InitIO fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:_PR_InitLinker fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:PR_NewCondVar fun:_PR_InitThreads fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:ExpandMonitorCache fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:PR_NewCondVar fun:_PR_InitIO fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewMonitor fun:PR_NewNamedMonitor fun:_PR_InitLinker fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:malloc fun:PR_NewCondVar fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewMonitor fun:_PR_UnixInit fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewLock fun:_PR_InitEnv fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewLock fun:_PR_InitLayerCache fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewLock fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewLock fun:_PR_InitThreads fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewLock fun:_PR_InitCMon fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewLock fun:_PR_InitIO fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewLock fun:_PR_InitFdCache fun:_PR_InitIO fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewLock fun:PR_CreateStack fun:_PR_InitFdCache fun:_PR_InitIO fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all } { Memcheck:Leak fun:calloc fun:PR_NewLock fun:_PR_InitNet fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewLock fun:_PR_InitTime fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewLock fun:_PR_InitLog fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewLock fun:_PR_InitDtoa fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewLock fun:_PR_InitMW fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewLock fun:_PR_UnixInit fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:PR_NewMonitor fun:ExpandMonitorCache fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } { Memcheck:Leak fun:calloc fun:_PR_InitTPD fun:_PR_InitStuff fun:Curl_nss_init fun:curl_global_init fun:lr_global_init fun:test_handle obj:/usr/lib64/libcheck.so.0.0.0 fun:srunner_run_all fun:main } librepo-1.12.1/tests/000077500000000000000000000000001372027637700144105ustar00rootroot00000000000000librepo-1.12.1/tests/CMakeLists.txt000066400000000000000000000014111372027637700171450ustar00rootroot00000000000000SET (librepotest_SRCS fixtures.c test_checksum.c test_downloader.c test_gpg.c test_handle.c test_lrmirrorlist.c test_main.c test_metalink.c test_mirrorlist.c test_package_downloader.c test_repoconf.c test_repomd.c test_repo_zck.c testsys.c test_url_substitution.c test_util.c test_version.c ) #ADD_LIBRARY(testsys STATIC testsys.c) #SET_TARGET_PROPERTIES(testsys PROPERTIES COMPILE_FLAGS -fPIC) ADD_EXECUTABLE(test_main ${librepotest_SRCS}) TARGET_LINK_LIBRARIES(test_main librepo ${CHECK_LIBRARY} ) CONFIGURE_FILE("run_tests.sh.in" "${CMAKE_BINARY_DIR}/tests/run_tests.sh") ADD_TEST(test_main run_tests.sh) IF (ENABLE_PYTHON_TESTS) ADD_SUBDIRECTORY (python) ENDIF() librepo-1.12.1/tests/README.rst000066400000000000000000000042321372027637700161000ustar00rootroot00000000000000C tests ======= C tests uses the Check unit testing framework (http://check.sourceforge.net/). Run (from your checkout dir):: build/tests/test_main tests/test_data/ *Tip:* Use Valgrind (http://valgrind.org/) with C tests:: valgrind build/tests/test_main tests/test_data/ Files of C tests ================ **test_data/** * Directory with testing repositories, metalink files, etc. **CMakeLists.txt** * CMake config file **fixtures.c fixtures.h** * Global test data (global test variable, setup and tear down function(s), ...) **testsys.c testsys.h** * Global test configuration, expected results (constants in testsys.h) and useful functions (functions in testsys.h) **test_main.c** * Main file **test_*.c test_*.h** * Test suites Python tests with Flask ======================= Some python tests use **Flask** python framework (http://flask.pocoo.org/) to simulate web server. The server is started automatically during that tests. *TestCases with Flask inherit from TestCaseWithApp class.* The Flask is then set as the app to the test case by 'application' class attribute. If you want to start server manually:: $ python python/tests/servermock/server.py Url examples ------------ http://127.0.0.1:5000/yum/auth_basic/static/01/repodata/repomd.xml * This url provides basic authentification http://127.0.0.1:5000/yum/badgpg/static/01/repodata/repomd.xml.asc * This url in fact returns .../repodata/repomd.xml.asc.bad file etc.. Modularity of tests with Flask ------------------------------ Flask tests are intended to be modular. Modularity is provided by use of http://flask.pocoo.org/docs/blueprints/ Currently there is only one module (blueprint) for yum repo mocking in servermock/yum_mock. Repos for test with Flask ------------------------- Currently each module (blueprint) uses its own data (repositories, packages, ..). E.g. for yum mocking module: servermock/yum_mock/static/ Configuration and globals for tests with Flask ---------------------------------------------- Each module (blueprint) has its own configuration and globals which uses and provides to tests which use the module. E.g. for yum mocking module: servermock/yum_mock/config.py librepo-1.12.1/tests/fixtures.c000066400000000000000000000032051372027637700164250ustar00rootroot00000000000000#define _XOPEN_SOURCE #include #include #include #include "librepo/util.h" #include "fixtures.h" #include "testsys.h" /* define the global variable */ struct TestGlobals_s test_globals; void test_log_handler_cb(G_GNUC_UNUSED const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, G_GNUC_UNUSED gpointer user_data) { time_t rawtime; char buffer[255]; gchar *level = ""; switch(log_level) { case G_LOG_LEVEL_ERROR: level = "ERROR"; break; case G_LOG_LEVEL_CRITICAL: level = "CRITICAL"; break; case G_LOG_LEVEL_WARNING: level = "WARNING"; break; case G_LOG_LEVEL_MESSAGE: level = "MESSAGE"; break; case G_LOG_LEVEL_INFO: level = "INFO"; break; case G_LOG_LEVEL_DEBUG: level = "DEBUG"; break; default: level = "UNKNOWN"; break; } time(&rawtime); strftime(buffer, 254, "%H:%M:%S", localtime(&rawtime)); g_printerr("%s: %s %s\n", buffer, level, message); } void lr_assert_strv_eq(const gchar * const *strv, ...) { va_list args; ck_assert_msg(strv != NULL, "NULL isn't strv"); va_start (args, strv); gchar **strv_p = (gchar **) strv; for (; *strv_p; strv_p++) { gchar *s = va_arg (args, gchar*); ck_assert_msg(s != NULL, "Lengths of lists are not the same"); ck_assert_str_eq(*strv_p, s); } // strv list already ended, check if we really expected the end ck_assert_msg(va_arg(args, gchar*) == NULL, "Lengths of lists are not the same"); va_end (args); } librepo-1.12.1/tests/fixtures.h000066400000000000000000000012051372027637700164300ustar00rootroot00000000000000#ifndef LR_FIXTURES_H #define LR_FIXTURES_H #include #include struct TestGlobals_s { char *testdata_dir; char *tmpdir; }; /* global data used to pass values from fixtures to tests */ extern struct TestGlobals_s test_globals; void test_log_handler_cb(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data); void lr_assert_strv_eq(const gchar * const *strv, ...); /* Old versions of check.h don't have this, just use g_assert() and drop the message */ #ifndef ck_assert_msg #define ck_assert_msg(exp, ...) g_assert (exp) #endif #endif /* FIXTURES_H */ librepo-1.12.1/tests/python/000077500000000000000000000000001372027637700157315ustar00rootroot00000000000000librepo-1.12.1/tests/python/CMakeLists.txt000066400000000000000000000000311372027637700204630ustar00rootroot00000000000000ADD_SUBDIRECTORY (tests) librepo-1.12.1/tests/python/tests/000077500000000000000000000000001372027637700170735ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/CMakeLists.txt000066400000000000000000000002001372027637700216230ustar00rootroot00000000000000CONFIGURE_FILE("run_nosetests.sh.in" "run_nosetests.sh") ADD_TEST(test_python run_nosetests.sh -s ${CMAKE_CURRENT_SOURCE_DIR}) librepo-1.12.1/tests/python/tests/__init__.py000066400000000000000000000000001372027637700211720ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/base.py000066400000000000000000000054311372027637700203620ustar00rootroot00000000000000import sys import time import socket import ctypes import os.path import requests from multiprocessing import Process, Value from tests.servermock.server import app try: import unittest2 as unittest except ImportError: import unittest import librepo try: from gpg import Context except ImportError: import gpgme class Context(object): def __init__(self): self.__dict__["ctx"] = gpgme.Context() def __enter__(self): return self def __exit__(self, type, value, tb): pass @property def armor(self): return self.ctx.armor @armor.setter def armor(self, value): self.ctx.armor = value def op_import(self, key_fo): self.ctx.import_(key_fo) def op_export(self, pattern, mode, keydata): self.ctx.export(pattern, keydata) def __getattr__(self, name): return getattr(self.ctx, name) MOCKURL_TEMPLATE = "http://127.0.0.1:%d/" TEST_DATA = os.path.normpath(os.path.join(__file__, "../../../test_data")) class TestCase(unittest.TestCase): pass class TestCaseWithApp(TestCase): application = NotImplemented @classmethod def setUpClass(cls): cls.server = Process(target=cls.application.run) cls.server.start() time.sleep(0.5) @classmethod def tearDownClass(cls): cls.server.terminate() cls.server.join() def application(port): """Sometimes, the port is used, in that case, use different port""" while True: try: port_val = port.value app._librepo_port = port_val # Store used port into Flask app app.run(port=port_val) except socket.error as e: if e.errno == 98: # Address already in use port.value += 1 continue raise break class TestCaseWithFlask(TestCase): _TS_PORT = Value(ctypes.c_int, 5000) MOCKURL = None PORT = -1 @classmethod def setUpClass(cls): cls.server = Process(target=application, args=(cls._TS_PORT,)) cls.server.start() cls.MOCKURL = MOCKURL_TEMPLATE % cls._TS_PORT.value cls.PORT = cls._TS_PORT.value # Wait for the server to start (max 5 seconds) for i in range(50): try: requests.get(cls.MOCKURL, timeout=0.1) break except (requests.exceptions.ConnectionError): time.sleep(0.1) except (requests.exceptions.Timeout): pass else: cls.tearDownClass() raise Exception("Server didn't start even after 5 seconds.") @classmethod def tearDownClass(cls): cls.server.terminate() cls.server.join() librepo-1.12.1/tests/python/tests/run_nosetests.sh.in000077500000000000000000000003001372027637700227430ustar00rootroot00000000000000LD_LIBRARY_PATH="${CMAKE_BINARY_DIR}/librepo/:" PYTHONPATH="${CMAKE_BINARY_DIR}/librepo/python/python${PYTHON_VERSION_MAJOR}" ${PYTHON_EXECUTABLE} -m nose -s -v "${CMAKE_CURRENT_SOURCE_DIR}/" librepo-1.12.1/tests/python/tests/servermock/000077500000000000000000000000001372027637700212535ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/__init__.py000066400000000000000000000000001372027637700233520ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/server.py000066400000000000000000000017261372027637700231410ustar00rootroot00000000000000from flask import Flask from optparse import OptionParser try: from yum_mock.yum_mock import yum_mock except (ValueError, ImportError): from .yum_mock.yum_mock import yum_mock app = Flask(__name__) #app.register_blueprint(working_repo) app.register_blueprint(yum_mock, url_prefix='/yum') if __name__ == '__main__': parser = OptionParser("%prog [options]") parser.add_option( "-d", "--debug", action="store_true", ) parser.add_option( "-p", "--port", default=5000, type="int", ) parser.add_option( "-n", "--host", default="127.0.0.1", ) parser.add_option( "--passthrough_errors", action="store_true", ) options, args = parser.parse_args() kwargs = { "threaded": True, "debug": options.debug, "port": options.port, "host": options.host, "passthrough_errors": options.passthrough_errors, } app.run(**kwargs) librepo-1.12.1/tests/python/tests/servermock/yum_mock/000077500000000000000000000000001372027637700230765ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/__init__.py000066400000000000000000000000001372027637700251750ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/config.py000066400000000000000000000041531372027637700247200ustar00rootroot00000000000000REPOS_YUM = "yum/static/" HARMCHECKSUM = "yum/harm_checksum/%s/" MISSINGFILE = "yum/not_found/%s/" BADURL = "yum/badurl/" BADGPG = "yum/badgpg/" AUTHBASIC = "yum/auth_basic/" PARTIAL = "yum/partial/" AUTH_USER = "admin" AUTH_PASS = "secret" REPO_YUM_01_PATH = REPOS_YUM+"01/" REPO_YUM_01_REPODATA = REPO_YUM_01_PATH+"repodata/" REPO_YUM_01_REPOMD = REPO_YUM_01_REPODATA+"repomd.xml" REPO_YUM_01_PATH_VAR = REPOS_YUM+"$version/" REPO_YUM_01_VARSUB_LIST = [("version", "01")] REPO_YUM_02_PATH = REPOS_YUM+"02/" REPO_YUM_03_PATH = REPOS_YUM+"03/" REPO_YUM_04_PATH = REPOS_YUM+"04/" METALINK_DIR = "yum/static/metalink/" METALINK_GOOD_01 = METALINK_DIR+"good_01.xml" METALINK_GOOD_02 = METALINK_DIR+"good_02.xml" METALINK_BADFILENAME = METALINK_DIR+"badfilename.xml" METALINK_BADCHECKSUM = METALINK_DIR+"badchecksum.xml" METALINK_NOURLS = METALINK_DIR+"nourls.xml" METALINK_BADFIRSTURL = METALINK_DIR+"badfirsturl.xml" METALINK_BADFIRSTHOST = METALINK_DIR+"badfirsthost.xml" METALINK_FIRSTURLHASCORRUPTEDFILES = METALINK_DIR+"firsturlhascorruptedfiles.xml" METALINK_VARSUB = METALINK_DIR+"varsub.xml" METALINK_VARSUB_LIST = [("version", "01")] METALINK_WITH_ALTERNATES = METALINK_DIR+"metalink_with_alternates.xml" METALINK_MIRRORS_01 = METALINK_DIR+"mirrors_01.xml" MIRRORLIST_DIR = "yum/static/mirrorlist/" MIRRORLIST_GOOD_01 = MIRRORLIST_DIR+"good_01" MIRRORLIST_GOOD_02 = MIRRORLIST_DIR+"good_02" MIRRORLIST_NOURLS = MIRRORLIST_DIR+"nourls" MIRRORLIST_BADFIRSTURL = MIRRORLIST_DIR+"badfirsturl" MIRRORLIST_FIRSTURLHASCORRUPTEDFILES = MIRRORLIST_DIR+"firsturlhascorruptedfiles" MIRRORLIST_VARED = MIRRORLIST_DIR+"$var" MIRRORLIST_VARED_LIST = [("var", "varsub"), ("version", "01")] MIRRORLIST_VARSUB = MIRRORLIST_DIR+"varsub" MIRRORLIST_VARSUB_LIST = [("version", "01")] # PACKAGE__ REPO_YUM_01_PACKAGES = REPO_YUM_01_PATH PACKAGE_01_01 = "filesystem-2.4.44-1.fc16.i686.rpm" PACKAGE_01_01_SHA256 = "3a2d4370e617056c9f76ec9ad1a7c699db8da90d3cc860943035ca0fda8136f4" REPO_YUM_03_PACKAGES = REPO_YUM_03_PATH PACKAGE_03_01 = "Archer-3.4.5-6.x86_64.rpm" REPO_YUM_04_PACKAGES = REPO_YUM_04_PATH PACKAGE_04_01 = "empty-0-0.src.rpm" librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/000077500000000000000000000000001372027637700243655ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/01/000077500000000000000000000000001372027637700246055ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/01/filesystem-2.4.44-1.fc16.i686.rpm000066400000000000000000040204741372027637700316420ustar00rootroot00000000000000filesystem-2.4.44-1.fc16>t  DH`pNAN+.|QgykMS[Bl-9yxMLNJ!GblyOꎶJerC}3i> l^9!& H0̯` 5A#Pm\QF(WJJV+N$pp!oXSE-ڨغaeCLOmT(WtZ`M{+dOq,&A Q4 p^Hs+뷤̏'7iS>b̀iπLOz2E*`!VLY[*ǫaOi'fFLTq5*q(ʎ26v0OK3U%p4_ =9'JwE܆TH|fpTb\*n#dK_[ay-`~]ΉM #HJs'E 7E+~zE\'{J3.2fr3@PU1iO|7c4d6370e62e5bf3b445bc31f30e70aaf673f9d26ԉNAN+/m*J+=Vj4ZBrM9!zPX kOtM ¿l$@vJH%ݎ-@, d;c \/f[vK | uf!-R+@9\|m\3fOxl-z%KDgSA P-n4ŨiZZg%o?^iQ:o}eѾ\- ELC8`SZhܪ!C @^N \t'}vXkcD>:?d   I(,LP_n|  88 V(8 ǀ8 08 8 8L8855(8(8s9s:,s>FvGF|8H),8I 8X Y \ 8] ̘8^ =b q`d qe rf rl rt r 8u T8v7w78x<8Cfilesystem2.4.441.fc16The basic directory layout for a Linux systemThe filesystem package is one of the basic packages that is installed on a Linux system. Filesystem contains the basic directory layout for a Linux operating system, including the correct permissions for the directories.N2t]x86-01.phx2.fedoraproject.orgFedora ProjectFedora ProjectPublic DomainFedora ProjectSystem Environment/Basehttps://fedorahosted.org/filesystemlinuxi686posix.symlink("../run", "/var/run") posix.symlink("../run/lock", "/var/lock")0@  AmAmAmAAAAAAAAAAAAAAAAAAmAmAmAAAAmAhAAAmAACAAmAAAAmAmAmAmAmAmAmAmAmAmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmAAAAAAAAAAAmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAN2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t3N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t3N2t3N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t3N2t3N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t3N2t3N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t3N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t4N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2N2t2../var/tmpspool/mailHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@rootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootrootmailrootrootfilesystem-2.4.44-1.fc16.src.rpmfilesystemfilesystem(x86-32)     rpmlib(BuiltinLuaScripts)rpmlib(CompressedFileNames)rpmlib(FileDigests)rpmlib(PayloadFilesHavePrefix)setuprpmlib(PayloadIsXz)4.2.2-13.0.4-14.6.0-14.0-15.2-14.9.0N2N @MӴMM@MlMgMQ0@M5L@KEK@KKJęJ@JF@J Jyt@Jx"Jv@Ji@JhPJhPJUJT@Im@I@IIHpH`H_@HXH;H2@G G@GFGFF\FVFU~@F$FM@F E5@EVE%E+@E+@D@Do@C@CCqC|pCoACBBz BfD@B.@A[@@PT@Dw@@,@@?@?>@>.==@=y@=@<<<0;;zd@;w@;r{@;q);n;T(;J@;7';33@;1;l:9x;@9l]9Y9Q9N @9Kh@9<96P@9*r987@6@666L@66W5I@5Ds@4:@3}@3T@Ondrej Vasik 2.4.44-1Ondrej Vasik 2.4.43-1Ondrej Vasik 2.4.42-1Ondrej Vasik 2.4.41-1Ondrej Vasik 2.4.40-1Ondrej Vasik 2.4.39-1Ondrej Vasik 2.4.38-1Fedora Release Engineering Adam Jackson 2.4.37-1Ondrej Vasik 2.4.36-1Ondrej Vasik 2.4.35-1Ondrej Vasik 2.4.34-1Ondrej Vasik 2.4.33-1Ondrej Vasik 2.4.32-1Ondrej Vasik 2.4.31-1Karsten Hopp 2.4.30-2Ondrej Vasik 2.4.30-1Ondrej Vasik 2.4.29-1Ondrej Vasik 2.4.28-1Adam Jackson 2.4.27-1Ondrej Vasik 2.4.26-1Fedora Release Engineering - 2.4.25-2Adam Jackson 2.4.25-1Adam Jackson 2.4.24-1Ondrej Vasik - 2.4.23-1Ondrej Vasik - 2.4.22-1Phil Knirsch - 2.4.21-1Fedora Release Engineering - 2.4.20-2Phil Knirsch - 2.4.20-1Phil Knirsch - 2.4.19-1Phil Knirsch - 2.4.18-1Phil Knirsch - 2.4.17-1Phil Knirsch - 2.4.16-1Phil Knirsch - 2.4.15-1Phil Knirsch - 2.4.14-1Phil Knirsch - 2.4.13-1Phil Knirsch - 2.4.12-1Fedora Release Engineering - 2.4.11-2Phil Knirsch 2.4.11-1Phil Knirsch 2.4.10-1Phil Knirsch 2.4.9-1Phil Knirsch 2.4.8-1Phil Knirsch 2.4.7-1Phil Knirsch - 2.4.6-1Peter Jones - 2.4.5-1Jeremy Katz - 2.4.4-1Phil Knirsch - 2.4.3-1Phil Knirsch - 2.4.2-1Phil Knirsch - 2.4.1-1Bill Nottingham - 2.4.0-1Phil Knirsch - 2.3.8-1Jesse Keating - 2.3.7-2.1Phil Knirsch - 2.3.7-2Jesse Keating - 2.3.7-1.2.1Jesse Keating - 2.3.7-1.2Jesse Keating Bill Nottingham - 2.3.7-1Bill Nottingham - 2.3.6-1Bill Nottingham - 2.3.5-1Bill Nottingham - 2.3.4-1Peter Jones - 2.3.3-1John Dennis - 2.3.2-1Bill Nottingham 2.3.1-1Bill Nottingham 2.3.0-1Elliot Lee Bill Nottingham 2.2.4-1Elliot Lee Elliot Lee Bill Nottingham 2.2.3-1Bill Nottingham 2.2.2-1Than Ngo 2.2.1-5Elliot Lee Tim Powers Tim Waugh 2.2.1-2Bill Nottingham 2.2.1-1Bill Nottingham Tim Powers Tim Powers Tim Powers Bill Nottingham Bill Nottingham Bill Nottingham Bill Nottingham Bill Nottingham Jeff Johnson Bill Nottingham Bill Nottingham Bill Nottingham Bill Nottingham Bill Nottingham Florian La Roche Bill Nottingham Bill Nottingham Prospector Preston Brown Preston Brown Bill Nottingham Bill Nottingham Bill Nottingham Preston Brown Trond Eivind Glomsrød Preston Brown Jakub Jelinek Preston Brown Cristian Gafton Jeff Johnson Preston Brown Preston Brown Bill Nottingham Bill Nottingham Erik Troan Prospector System Erik Troan Erik Troan Erik Troan - drop ownership of /selinux - moved to /sys/fs/selinux(#726528)- add various languages to lang-exceptions(#620063)- Pre: require setup again (#705443)- drop filesystem.conf file (#694688)- create /run/lock as 755 root:root (#693394)- add /run to filesystem (#692124) - minor spec file cleanup- do /var/lock/subsys directory systemd way via tmpfiles.d conf file (#656586)- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild- Drop Prov/Obs: xorg-x11-filesystem and pm-utils-filesystem, both last seen in Fedora 11. - Remove explicit BuildRoot.- own /usr/lib/sse2 even on 64-bit (#636748)- change permissions on /var/lock from 775 root:lock to 755 root:root (#581884)- drop ownership for /mnt/{floppy,cdrom} subdirs(#173854)- do own /usr/share/aclocal (#533962)- added sr@ijekavian and sr@ijekavianlatin into lang exceptions- added zh_CN.GB2312 to lang exceptions(#487568)- fix typo in Provides- adjust directory rights for usage of capabilities(#517575)- iso_codes package no longer provides tab files, do generate them on fly with sed (thanks D. Tardon)- Provide/obsolete pm-utils-filesystem, own dirs for pm-utils hooks(#515362) - Do own man sections for /usr/share/man/ dirs (#220265) - Do own /usr/share/sounds (#515485)- Prov/Obs: xorg-x11-filesystem- Do own /usr/share/man/ directories (ghosted, missingok) - #220265- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild- Remove explicit /usr/lib/X11, everything uses %_libdir now.- Added /usr/share/X11- do own /usr/src/debug (#214983)- do own interface description directory /usr/share/idl(#451719) - add a few missing lang-exceptions to filelist(#508309)- Added /usr/share/backgrounds (#487957) - Added /usr/share/ghostscript/{conf.d} (#302521)- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild- Removed ownership of fonts directories (#477046)- Added augeas lenses dir (#461317)- Added comment with raw format lang-exception URL- Added URL for lang-exception source (#225752)- Dropped /etc/news again as we're handling it now correctly (#437462) - Filesystem is now an official fedorahosted project, part of the review changes (#225752) - Removed duplicate entry in lang_exceptions for ca_ES@valencian (#225752)- First round of Fedora package review changes (#225752)- Added /usr/src/kernels to owned and created dirs (#442283)- Added /etc/news to owned and created directories- Added be@latin to lang-exceptions (#231737) - Added /usr/share/man{0,1,3]p to owned files (#233879) - Added /usr/share/fonts to owned files (#302141) - Renamed sr@Latn to sr@latin (#436887)- Autorebuild for GCC 4.3- Added /etc/X11/fontpath.d and dropped /etc/X11/sysconfig /etc/X11/serverconfig (#251707)- Replaced gtk-doc with gnome (#247276)- Fixed nasty typo for /etc directories (#241525)- Fixed description to avoid trademark issues (#234093)- Added /etc/fonts/conf.d and /usr/share/themes (#239246) - Removed /etc/xdg/menus, already owned by redhat-menus (#228779)- Added several more /usr/share directories (#222905)- add /usr/local/share/applications- add /etc/xdg/autostart- Fixed typo for new /etc/xdg entries (#224052) - One more tiny specile cleanup- Added several missing unowned directories (#224052) - Tiny specfile cleanups- Dropped the obsolete directories /usr/lib{,64}/gcc-lib (#220235)- create and own /usr/share/locale/*/LC_MESSAGES (#196669)- Added the manXx directories to the ownership of filesystem (#208121)- rebuild- Fixed games location according to FHS 2.1 (#165425) - Added {_libdir}/sse2 to owned files (#192853) - Added /dev to owned files (#192860) - Added {_datadir}/icons to owned files (#195911) - Dropped obsolete /etc/X11/starthere (#191163)- bump again for double-long bug on ppc(64)- rebuilt for new gcc4.1 snapshot and glibc changes- rebuilt- actually, *do* package /usr/lib/X11, etc, but as directories - remove /usr/X11R6 heirarchy- don't package /usr/lib/X11 or /usr/bin/X11 symlinks- package / (#165797)- ship /usr/share/games (#158433, )- remove /initrd, since mkinitrd doesn't use it anymore by default- add /etc/pki, a place to store keys and certificates- don't ship /usr/lib64/X11 in general (#147077)- add /media, /srv- rebuilt- move /selinux here from SysVinit- rebuilt- rebuilt- move /usr/dict to /usr/share/dict (#113645) - add /usr/lib/locale, /var/empty (#108686,#113036) - add */%{_lib}/tls (#113050)- add /sys- add /usr/share/xsessions- rebuilt- rebuilt- Really fix /var/mail- fix /var/mail- make arch specific, handle lib/lib64 stuff - add /usr/libexec, /usr/share/applications- automated rebuild- automated rebuild- automated rebuild- %ghost /mnt/cdrom, /mnt/floppy (fixes #52046)- add /usr/X11R6/share (#51830)- prereq a particular version of the setup package- remove /mnt/cdrom, /mnt/floppy (updfstab will create them if needed) - make it noarch again- /var/lock needs to be root.lock, not lock.lock- lock.lock ownership, 0775 permissions, for /var/lock.- add /etc/sysconfig, /var/yp, /usr/share/pixmaps- add stuff under /etc/X11 - remove extraneous /usr/X11R6/doc (#47490)- don't conflict with rpm- don't own /var/lib/rpm (#43315) - add some stuff in /usr/local (#36522)- add /initrd- remove noarch - do not include /mnt/cdrom and /mnt/floppy for s390/s390x- take the group write off of /var/lock- add /usr/share/empty- automatic rebuild- remove /usr/doc- remove /usr/info- remove /usr/man- /var/spool/lpd should have normal perms (#12272)- add /etc/skel- add /var/spool/lpd to filesystem, owned by user/group lp, tight permissions- Added /etc/xinetd.d- /etc/opt, /usr/share/{info,man/man*,misc,doc} (FHS 2.1) - added /var/games. Data should move from /var/lib/games to there (FHS 2.1) - bump version up to 2.0 already!- removed /var/state, added /var/opt, /var/mail for FHS 2.1 compliance- added /opt, /var/state, /var/cache for FHS compliance (#3966)- auto rebuild in the new build environment (release 4)- don't carry X11R6.1 as directory on sparc. - /var/tmp/build root (#811)- font directory didn't belong, which I previously misunderstood. removed.- /usr/share/fonts/default added.- put /mnt/cdrom back in- Changed /root to 0750- added /var/db - set attributes in the spec file; don't depend on the ones in the cpio archive - use a tarball instead of a cpioball- translations modified for de, fr, tr- made a noarch package- added /- Changed /proc to 555 - Removed /var/spool/mqueue (which is owned by sendmail)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            &&''''''()*+,&''XYZ     D !"#$%_`abcdefghijkpqlmnorstuvwz{|}~67  !"#()$%  &'  bc,-*+./0123456789<=>?@ABCDE:;HIJK\]LMNOPQRSTU"#$%89VWXYZ[^_jk`ade|}jkhilmfgbcnopqrstuvwxy~z{      !"#$%&'()*+,-@A./DE<=016789JK>?BCFGHI45LMNO23PQ:;VWTUXYZ[RS\]^_`adehinorstuvwz{|}pqbcxyfglmjk     01"#&'$%&' !()*+,-FG./67234589:;<=>?@ABCDEFGHIJKLMPQNOTURSVWXYbcZ[\]^_`adefghilmnopqrstuxyz{|}vw ~  ~  xy01()*+,-./     01 !"#,-&'$%()67./*+234589:;<=>?@ADEBCFGHIJKLMNO4523PQRSTUVWXYfgZ[\]^_`adehijklmnopqrstuvw~xyz{|} !&=  :;<                   4 ! 5 + " , # 6 - $ . % / & 0 ' 1 ( 2 ) 3 * 7 K 8 L B 9 C : M D ; E < F = G > H ? I @ J A N b O c Y P Z Q d [ R \ S ] T ^ U _ V ` W a X vwxyz{|}~ e y f z p g q h { r i s j t k u l v m w n x o | } ~                     1  2 (  ) 3 * ! + " , # - $ . % / & 0 ' 4 H 5 I ? 6 @ 7 J A 8 B 9 C : D ; E < F = G > K _ L ` V M W N a X O Y P Z Q [ R \ S ] T ^ U b v c w m d n e x o f p g q h r i s j t k u l y z { | } ~                     .  / %  &  0 '  (  ) * ! + " , # - $ 1 E 2 F < 3 = 4 G > 5 ? 6 @ 7 A 8 B 9 C : D ; H \ I ] S J T K ^ U L V M W N X O Y P Z Q [ R _ s ` t j a k b u l c m d n e o f p g q h r i v w x y z { | } ~                     +  , "  #  - $  %  &  '  (  ) * ! \ ]   ^  _   `  a  b c d e  . B / C 9 0 : 1 D ; 2 < 3 = 4 > 5 ? 6 @ 7 A 8 E Y F Z P G Q H [ R I S J T K U L V M W N X O  $  %     &        !  "  #  > R ? S I @ J A T K B L C M D N E O F P G Q H l m w n x o y p z q { r | s } t ~ u  v          8 L 9 M C : D ; N E < F = G > H ? I @ J A K B $ 8 % 9 / & 0 ' : 1 ( 2 ) 3 * 4 + 5 , 6 - 7 . !  "     #              i } j ~ t k u l  v m w n x o y p z q { r | s ; O < P F = G > Q H ? I @ J A K B L C M D N E R f S g ] T ^ U h _ V ` W a X b Y c Z d [ e \ ' ; ( < 2 ) 3 * = 4 + 5 , 6 - 7 . 8 / 9 0 : 1  MaNbXOYPcZQ[R\S]T^U_V`W                           ! 5 " 6 , # - $ 7 . % / & 0 ' 1 ( 2 ) 3 * 4 + f z g { q h r i | s j t k u l v m w n x o y p      +,"#-$%&'() *!.B/C90:1D;2<3=4>5?6@7A8 } ~  L ` M a W N X O b Y P Z Q [ R \ S ] T ^ U _ V                          2  3 ) * ! 4 + " , # - $ . % / & 0 ' 1 ( 5 I 6 J @ 7 A 8 K B 9 C : D ; E < F = G > H ? c w d x n e o f y p g q h r i s j t k u l v m                  /  0 &  '  1 (  ) * ! + " , # - $ . % 2 F 3 G = 4 > 5 H ? 6 @ 7 A 8 B 9 C : D ; E < I ] J ^ T K U L _ V M W N X O Y P Z Q [ R \ S ` t a u k b l c v m d n e o f p g q h r i s j w x y z { | } ~   ,  - #  $  . %  &  '  (  ) * ! + " / C 0 D : 1 ; 2 E < 3 = 4 > 5 ? 6 @ 7 A 8 B 9 F Z G [ Q H R I \ S J T K U L V M W N X O Y P ] q ^ r h _ i ` s j a k b l c m d n e o f p g t u  v w x y z { | } ~ EYFZPGQH[RISJTKULVMWNXO   )  *  !  + "  #  $  %  &  '  (                 , @ - A 7 . 8 / B 9 0 : 1 ; 2 < 3 = 4 > 5 ? 6 C W D X N E O F Y P G Q H R I S J T K U L V M\p]qg^h_ri`jakblcmdneofst~uvwxyz{|} Z n [ o e \ f ] p g ^ h _ i ` j a k b l c m d q r | s } t ~ u  v w x y z {              &  '     (    !  "  #  $  %  ) = * > 4 + 5 , ? 6 - 7 . 8 / 9 0 : 1 ; 2 < 3 W k X l b Y c Z m d [ e \ f ] g ^ h _ i ` j a n o y p z q { r | s } t ~ u  v w x            )* !+"#$%&'(,@-A7.8/B90:1;2<3=4>5?6          +?,@6-7.A8/90:1;2<3=4>5() *!"#$%&'CWDXNEOFYPGQHRISJTKULVMZn[oe\f]pg^h_i`jakblcmdBVCWMDNEXOFPGQHRISJTKULYmZnd[e\of]g^h_i`jakblcpq{r|s}t~uvwxyz     %&' !"#$(<)=3*4+>5,6-7.8/90:1;2?S@TJAKBULCMDNEOFPGQHRIVjWkaXbYlcZd[e\f]g^h_i`mnxoypzq{r|s}t~uvwqr|s}t~uvwxyz{ U i V j ` W a X k b Y c Z d [ e \ f ] g ^ h _AUBVLCMDWNEOFPGQHRISJTK          )=*>4+5,?6-7.8/90:1;2<3"#$ !&'( !"#$%@TAUKBLCVMDNEOFPGQHRISJ     WkXlbYcZmd[e\f]g^h_i`janoypzq{r|s}t~uvwx%9&:0'1(;2)3*4+5,6-7.8/<P=QG>H?RI@JAKBLCMDNEOFST^U_V`WaXbYcZd[e\f]     #$% !"   &:';1(2)<3*4+5,6-7.8/90=Q>RH?I@SJAKBLCMDNEOFPGThUi_V`WjaXbYcZd[e\f]g^klvmwnxoypzq{r|s}t~u   !"3 4*!+"5,#-$.%/&0'1(2):N;OE<F=PG>H?I@JAKBLCMD"#$ !QeRf\S]Tg^U_V`WaXbYcZd[h|i}sjtk~ulvmwnxoypzq{r%9&:0'1(;2)3*4+5,6-7.8/<P=QG>H?RI@JAKBLCMDNEOF#7$8.%/&90'1(2)3*4+5,6-SgTh^U_Vi`WaXbYcZd[e\f]j~kulvmwnxoypzq{r|s}t6J7KA8B9LC:D;E<F=G>H?I@"6#7-$.%8/&0'1(2)3*4+5,      !9M:ND;E<OF=G>H?I@JAKBLCPdQe[R\Sf]T^U_V`WaXbYcZg{h|risj}tkulvmwnxoypzq~MaNbXOYPcZQ[R\S]T^U_V`Wdxeyofpgzqhrisjtkulvmwn   {|}~6J7KA8B9LC:D;E<F=G>H?I@MaNbXOYPcZQ[R\S]T^U_V`W01'(2) *!+",#-$.%/&     3 4*!+"5,#-$.%/&0'1(2)3G4H>5?6I@7A8B9C:D;E<F=J^K_ULVM`WNXOYPZQ[R\S]T     aubvlcmdwneofpgqhrisjtk{|}~dxeyofpgzqhrisjtkulvmwnxyz{|}~          5I6J@7A8KB9C:D;E<F=G>H?cwdxneofypgqhrisjtkulvm     2F3G=4>5H?6@7A8B9C:D;E<I]J^TKUL_VMWNXOYPZQ[R\S23) *!4+",#-$.%/&0'1(/0&'1() *!+",#-$.%L`MaWNXObYPZQ[R\S]T^U_V     ,-#$.%&'() *!+"/C0D:1;2E<3=4>5?6@7A8B9FZG[QHRI\SJTKULVMWNXOYP]q^rh_i`sjakblcmdneofpgtuvwxyz{|}~z{|}~                   wxyz{|}~CWDXNEOFYPGQHRISJTKULVM     )* !+"#$%&'(     qr|s}t~uvwxyz{@A B C DEF  ,@-A7.8/B90:1;2<3=4>5?6)=*>4+5,?6-7.8/90:1;2<3&'( !"#$%G)H* !+"#$%&'(,@-A7.8/B90:1;2<3=4>5?6CWDXNEOFYPGQHRISJTKULVMZn[oe\f]pg^h_i`jakblcmdZn[oe\f]pg^h_i`jakblcmd&'( !"#$%:';1(2)<3*4+5,6-7.8/90@TAUKBLCVMDNEOFPGQHRISJnoypzq{r|s}t~uvwxThUi_V`WjaXbYcZd[e\f]g^)=*>4+5,?6-7.8/90:1;2<3     #$% !"&:';1(2)<3*4+5,6-7.8/90qr|s}t~uvwxyz{=Q>RH?I@SJAKBLCMDNEOFPG           !"WkXlbYcZmd[e\f]g^h_i`ja     klvmwnxoypzq{r|s}t~u#7$8.%/&90'1(2)3*4+5,6-:N;OE<F=PG>H?I@JAKBLCMDQeRf\S]Tg^U_V`WaXbYcZd[h|i}sjtk~ulvmwnxoypzq{r O c P d Z Q [ R e \ S ] T ^ U _ V ` W a X b Y01'(2) *!+",#-$.%/&     3G4H>5?6I@7A8B9C:D;E<F=NbOcYPZQd[R\S]T^U_V`WaX     *+!",#$%&'() J^K_ULVM`WNXOYPZQ[R\S]T01'(2) *!+",#-$.%/&3G4H>5?6I@7A8B9C:D;E<F=eyfzpgqh{risjtkulvmwnxoJ^K_ULVM`WNXOYPZQ[R\S]Taubvlcmdwneofpgqhrisjtkxyz{|}~ @ T A U K B L C V M D N E O F P G Q H R I S Jaubvlcmdwneofpgqhrisjtkxyz{|}~     7K8LB9C:MD;E<F=G>H?I@JA-.$%/&'() *!+",#0D1E;2<3F=4>5?6@7A8B9C: 4!5+",#6-$.%/&0'1(2)3*G[H\RISJ]TKULVMWNXOYPZQ^r_si`jatkblcmdneofpgqhuvwxyz{|}~*+!",#$%&'()      DXEYOFPGZQHRISJTKULVMWN-A.B8/90C:1;2<3=4>5?6@7[o\pf]g^qh_i`jakblcmdners}t~uvwxyz{|     '() !"#$%&*>+?5,6-@7.8/90:1;2<3=4XlYmcZd[ne\f]g^h_i`jakbopzq{r|s}t~uvwxy     $%& !"#';(<2)3*=4+5,6-7.8/90:1>R?SI@JATKBLCMDNEOFPGQHUiVj`WaXkbYcZd[e\f]g^h_lmwnxoypzq{r|s}t~uv     G[H\RISJ]TKULVMWNXOYPZQ0D1E;2<3F=4>5?6@7A8B9C:-.$%/&'() *!+",#uvwxyz{|}~^r_si`jatkblcmdneofpgqh`taukblcvmdneofpgqhrisj*+!",#$%&'()      -A.B8/90C:1;2<3=4>5?6@7DXEYOFPGZQHRISJTKULVMWN    [o\pf]g^qh_i`jakblcmdners}t~uvwxyz{|UiVj`WaXkbYcZd[e\f]g^h_'() !"#$%&     *>+?5,6-@7.8/90:1;2<3=4AUBVLCMDWNEOFPGQHRISJTKopzq{r|s}t~uvwxyXlYmcZd[ne\f]g^h_i`jakb$%& !"#     ';(<2)3*=4+5,6-7.8/90:1>R?SI@JATKBLCMDNEOFPGQHlmwnxoypzq{r|s}t~uv           $8%9/&0':1(2)3*4+5,6-7.-A.B8/90C:1;2<3=4>5?6@7DXEYOFPGZQHRISJTKULVMWN[o\pf]g^qh_i`jakblcmdners}t~uvwxyz{|-.$%/&'() *!+",# !"# ;O<PF=G>QH?I@JAKBLCMDNEi}j~tkulvmwnxoypzq{r|sRfSg]T^Uh_V`WaXbYcZd[e\    !0D1E;2<3F=4>5?6@7A8B9C:  "6#7-$.%8/&0'1(2)3*4+5,9M:ND;E<OF=G>H?I@JAKBLC~g{h|risj}tkulvmwnxoypzq     6J7KA8B9LC:D;E<F=G>H?I@{|}~dxeyofpgzqhrisjtkulvmwn3 4*!+"5,#-$.%/&0'1(2)aubvlcmdwneofpgqhrisjtk01'(2) *!+",#-$.%/&     3G4H>5?6I@7A8B9C:D;E<F=xyz{|}~J^K_ULVM`WNXOYPZQ[R\S]T     -.$%/&'() *!+",#PdQe[R\Sf]T^U_V`WaXbYcZ0D1E;2<3F=4>5?6@7A8B9C:G[H\RISJ]TKULVMWNXOYPZQuvwxyz{|}~^r_si`jatkblcmdneofpgqhG[H\RISJ]TKULVMWNXOYPZQ^r_si`jatkblcmdneofpgqh     *+!",#$%&'() -A.B8/90C:1;2<3=4>5?6@7DXEYOFPGZQHRISJTKULVMWN[o\pf]g^qh_i`jakblcmdne     rs}t~uvwxyz{|'( !"#$%&     12() 3*!+",#-$.%/&0'4H5I?6@7JA8B9C:D;E<F=G>K_L`VMWNaXOYPZQ[R\S]T^Ubvcwmdnexofpgqhrisjtkulyz{|}~ z  {  | }  ~  uvwxyz{|}~     ./%&0'() *!+",#-$1E2F<3=4G>5?6@7A8B9C:D;H\I]SJTK^ULVMWNXOYPZQ[R_s`tjakbulcmdneofpgqhri=>?@ABCEGF]HYXI[JKLMNOP^QZRSTVUW\aaaaababaceaceachachadaadaadyadyaeaeafafaf_ZAaf_ZAafaafaafhafhainainakakakkakkalealealgalgaltaltamamam_ETam_ETanananganganpanpapaapaarararcarcarnarnarparpartartarwarwasasastastast_ESast_ESathathausausavavawaawaayayazazaz_IRaz_IRbababadbadbaibaibalbalbanbanbasbasbatbatbebebebebejbejbembemberberbgbgbg_BGbg_BGbhbhbhobhobibibikbikbinbinblablabmbmbnbnbn_INbn_INbntbntbobobrbrbrabrabsbsbtkbtkbuabuabugbugbynbyncacacacaca_ESca_ESca_ESca_EScadcadcaicaicarcarcaucaucececebcebcelcelchchchbchbchgchgchkchkchmchmchnchnchochochpchpchrchrchychycmccmccococopcopcpecpecpfcpfcppcppcrcrcrhcrhcrpcrpcscscs_CZcs_CZcsbcsbcucucuscuscvcvcycydadadakdakdardardaydaydedede_ATde_ATde_CHde_CHde_DEde_DEdefaultdefaultdeldeldendendgrdgrdindindoidoidradradsbdsbduaduadumdumdvdvdyudyudzdzeeeeefiefiegyegyekaekaelelel_GRel_GRelxelxenenenenenenenenen_AUen_AUen_CAen_CAen_GBen_GBen_NZen_NZen_USen_USenmenmeoeoeseses_ARes_ARes_CLes_CLes_COes_COes_CRes_CRes_DOes_DOes_ECes_ECes_ESes_ESes_GTes_GTes_HNes_HNes_MXes_MXes_NIes_NIes_PAes_PAes_PEes_PEes_PRes_PRes_SVes_SVes_UYes_UYes_VEes_VEetetet_EEet_EEeueueu_ESeu_ESewoewofafafa_IRfa_IRfanfanfatfatfffffififi_FIfi_FIfilfilfiufiufjfjfofofonfonfrfrfr_BEfr_BEfr_CAfr_CAfr_CHfr_CHfr_FRfr_FRfrmfrmfrofrofrrfrrfrsfrsfurfurfyfygagagaagaagaygaygbagbagdgdgemgemgezgezgilgilglglgl_ESgl_ESgmhgmhgngngohgohgongongorgorgotgotgrbgrbgrcgrcgswgswgugugvgvgwigwihahahaihaihawhawhehehe_ILhe_ILhihihilhilhimhimhithithmnhmnhohohrhrhr_HRhr_HRhsbhsbhththuhuhu_HUhu_HUhuphuphyhyhzhziaiaibaibaididieieigigiiiiijoijoikikiloiloincincineineinhinhioioirairairoiroisisititit_CHit_CHit_ITit_ITiuiujajaja_JPja_JPjbojbojprjprjrbjrbjvjvkakakaakaakabkabkackackamkamkarkarkawkawkbdkbdkgkgkhakhakhikhikhokhokikikjkjkkkkklklkmkmkmbkmbknknkokoko_KRko_KRkokkokkoskoskpekpekrkrkrckrckrlkrlkrokrokrukrukskskskskukukumkumkutkutkvkvkwkwkykylalaladladlahlahlamlamlblblezlezlglglililnlnlololollollozlozltltlululualualuiluilunlunluoluolusluslvlvlv_LVlv_LVmadmadmagmagmaimaimakmakmanmanmapmapmasmasmdfmdfmdrmdrmenmenmgmgmgamgamhmhmimimicmicminminmismismkmkmkhmkhmlmlmnmnmncmncmnimnimnomnomomomohmohmosmosmrmrmsmsms_MYms_MYmtmtmulmulmunmunmusmusmwlmwlmwrmwrmymymy_MMmy_MMmynmynmyvmyvnananahnahnainainapnapnbnbnb_NOnb_NOndndndsndsndsndsnds_DEnds_DEnenenewnewngngnianianicnicniuniunlnlnl_BEnl_BEnl_NLnl_NLnnnnnononognognonnonnqonqonrnrnsonsonubnubnvnvnwcnwcnynynymnymnynnynnyonyonzinziococojojomomororosososaosaotaotaotootopapapaapaapagpagpalpalpampampappappaupaupeopeophiphiphnphnpipiplplpl_PLpl_PLponponpraprapropropspsptptpt_BRpt_BRpt_PTpt_PTqaa-qtzqaa-qtzququrajrajrapraprarrarrmrmrnrnrororoaroaromromrururu_RUru_RUrupruprwrwsasasadsadsahsahsaisaisalsalsamsamsassassatsatscscscnscnscoscosdsdseseselselsemsemsgsgsgasgasgnsgnshnshnsisisidsidsiosiositsitskskslslsl_SIsl_SIslaslasmsmsmasmasmismismjsmjsmnsmnsmssmssnsnsnksnksososogsogsonsonsqsqsq_ALsq_ALsrsrsrsrsrsrsrsrsrsrsrsrsr_RSsr_RSsrnsrnsrrsrrssssssassaststsususuksuksussussuxsuxsvsvsv_SEsv_SEswswsycsycsyrsyrtatataitaitetetemtemtertertettettgtgththtititigtigtivtivtktktkltkltltltlhtlhtlitlitmhtmhtntntototogtogtpitpitrtrtststsitsitttttumtumtuptuptuttuttvltvltwtwtytytyvtyvudmudmugugugaugaukukuk_UAuk_UAumbumbundundururur_PKur_PKuzuzuzuzuzuzvaivaivevevivivovovotvotwawawakwakwalwalwarwarwaswaswenwenwowoxalxalxhxhyaoyaoyapyapyiyiyoyoypkypkzazazapzapzblzblzenzenzhzhzh_CNzh_CN.GB2312zh_CN.GB2312zh_CNzh_HKzh_HKzh_TWzh_TW.Big5zh_TW.Big5zh_TWzndzndzuzuzunzunzxxzxxzzazzaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabababababababababababababababababababababababaceaceaceaceaceaceaceaceaceaceaceaceaceaceaceaceaceaceaceaceaceaceaceachachachachachachachachachachachachachachachachachachachachachachachadaadaadaadaadaadaadaadaadaadaadaadaadaadaadaadaadaadaadaadaadaadaadaadyadyadyadyadyadyadyadyadyadyadyadyadyadyadyadyadyadyadyadyadyadyadyaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeafafafafafafafafafafafafafafafafafafafafafafafaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAaf_ZAafaafaafaafaafaafaafaafaafaafaafaafaafaafaafaafaafaafaafaafaafaafaafaafhafhafhafhafhafhafhafhafhafhafhafhafhafhafhafhafhafhafhafhafhafhafhainainainainainainainainainainainainainainainainainainainainainainainakakakakakakakakakakakakakakakakakakakakakakakakkakkakkakkakkakkakkakkakkakkakkakkakkakkakkakkakkakkakkakkakkakkakkalealealealealealealealealealealealealealealealealealealealealealealealgalgalgalgalgalgalgalgalgalgalgalgalgalgalgalgalgalgalgalgalgalgalgaltaltaltaltaltaltaltaltaltaltaltaltaltaltaltaltaltaltaltaltaltaltaltamamamamamamamamamamamamamamamamamamamamamamamam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETam_ETananananananananananananananananananananananananganganganganganganganganganganganganganganganganganganganganganganganpanpanpanpanpanpanpanpanpanpanpanpanpanpanpanpanpanpanpanpanpanpanpapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaararararararararararararararararararararararararcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarnarnarnarnarnarnarnarnarnarnarnarnarnarnarnarnarnarnarnarnarnarnarnarparparparparparparparparparparparparparparparparparparparparparparpartartartartartartartartartartartartartartartartartartartartartartartarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwasasasasasasasasasasasasasasasasasasasasasasasastastastastastastastastastastastastastastastastastastastastastastastast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESast_ESathathathathathathathathathathathathathathathathathathathathathathathausausausausausausausausausausausausausausausausausausausausausausausavavavavavavavavavavavavavavavavavavavavavavavawaawaawaawaawaawaawaawaawaawaawaawaawaawaawaawaawaawaawaawaawaawaawaayayayayayayayayayayayayayayayayayayayayayayayazazazazazazazazazazazazazazazazazazazazazazazaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRaz_IRbabababababababababababababababababababababababadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibalbalbalbalbalbalbalbalbalbalbalbalbalbalbalbalbalbalbalbalbalbalbalbanbanbanbanbanbanbanbanbanbanbanbanbanbanbanbanbanbanbanbanbanbanbanbasbasbasbasbasbasbasbasbasbasbasbasbasbasbasbasbasbasbasbasbasbasbasbatbatbatbatbatbatbatbatbatbatbatbatbatbatbatbatbatbatbatbatbatbatbatbebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebejbejbejbejbejbejbejbejbejbejbejbejbejbejbejbejbejbejbejbejbejbejbejbembembembembembembembembembembembembembembembembembembembembembembemberberberberberberberberberberberberberberberberberberberberberberberbgbgbgbgbgbgbgbgbgbgbgbgbgbgbgbgbgbgbgbgbgbgbgbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbg_BGbhbhbhbhbhbhbhbhbhbhbhbhbhbhbhbhbhbhbhbhbhbhbhbhobhobhobhobhobhobhobhobhobhobhobhobhobhobhobhobhobhobhobhobhobhobhobibibibibibibibibibibibibibibibibibibibibibibibikbikbikbikbikbikbikbikbikbikbikbikbikbikbikbikbikbikbikbikbikbikbikbinbinbinbinbinbinbinbinbinbinbinbinbinbinbinbinbinbinbinbinbinbinbinblablablablablablablablablablablablablablablablablablablablablablablabmbmbmbmbmbmbmbmbmbmbmbmbmbmbmbmbmbmbmbmbmbmbmbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbn_INbntbntbntbntbntbntbntbntbntbntbntbntbntbntbntbntbntbntbntbntbntbntbntbobobobobobobobobobobobobobobobobobobobobobobobrbrbrbrbrbrbrbrbrbrbrbrbrbrbrbrbrbrbrbrbrbrbrbrabrabrabrabrabrabrabrabrabrabrabrabrabrabrabrabrabrabrabrabrabrabrabsbsbsbsbsbsbsbsbsbsbsbsbsbsbsbsbsbsbsbsbsbsbsbtkbtkbtkbtkbtkbtkbtkbtkbtkbtkbtkbtkbtkbtkbtkbtkbtkbtkbtkbtkbtkbtkbtkbuabuabuabuabuabuabuabuabuabuabuabuabuabuabuabuabuabuabuabuabuabuabuabugbugbugbugbugbugbugbugbugbugbugbugbugbugbugbugbugbugbugbugbugbugbugbynbynbynbynbynbynbynbynbynbynbynbynbynbynbynbynbynbynbynbynbynbynbyncacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacacaca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_ESca_EScadcadcadcadcadcadcadcadcadcadcadcadcadcadcadcadcadcadcadcadcadcadcadcaicaicaicaicaicaicaicaicaicaicaicaicaicaicaicaicaicaicaicaicaicaicaicarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcarcaucaucaucaucaucaucaucaucaucaucaucaucaucaucaucaucaucaucaucaucaucaucaucecececececececececececececececececececececececebcebcebcebcebcebcebcebcebcebcebcebcebcebcebcebcebcebcebcebcebcebcebcelcelcelcelcelcelcelcelcelcelcelcelcelcelcelcelcelcelcelcelcelcelcelchchchchchchchchchchchchchchchchchchchchchchchchbchbchbchbchbchbchbchbchbchbchbchbchbchbchbchbchbchbchbchbchbchbchbchgchgchgchgchgchgchgchgchgchgchgchgchgchgchgchgchgchgchgchgchgchgchgchkchkchkchkchkchkchkchkchkchkchkchkchkchkchkchkchkchkchkchkchkchkchkchmchmchmchmchmchmchmchmchmchmchmchmchmchmchmchmchmchmchmchmchmchmchmchnchnchnchnchnchnchnchnchnchnchnchnchnchnchnchnchnchnchnchnchnchnchnchochochochochochochochochochochochochochochochochochochochochochochochpchpchpchpchpchpchpchpchpchpchpchpchpchpchpchpchpchpchpchpchpchpchpchrchrchrchrchrchrchrchrchrchrchrchrchrchrchrchrchrchrchrchrchrchrchrchychychychychychychychychychychychychychychychychychychychychychychycmccmccmccmccmccmccmccmccmccmccmccmccmccmccmccmccmccmccmccmccmccmccmccocococococococococococococococococococococococopcopcopcopcopcopcopcopcopcopcopcopcopcopcopcopcopcopcopcopcopcopcopcpecpecpecpecpecpecpecpecpecpecpecpecpecpecpecpecpecpecpecpecpecpecpecpfcpfcpfcpfcpfcpfcpfcpfcpfcpfcpfcpfcpfcpfcpfcpfcpfcpfcpfcpfcpfcpfcpfcppcppcppcppcppcppcppcppcppcppcppcppcppcppcppcppcppcppcppcppcppcppcppcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrhcrhcrhcrhcrhcrhcrhcrhcrhcrhcrhcrhcrhcrhcrhcrhcrhcrhcrhcrhcrhcrhcrhcrpcrpcrpcrpcrpcrpcrpcrpcrpcrpcrpcrpcrpcrpcrpcrpcrpcrpcrpcrpcrpcrpcrpcscscscscscscscscscscscscscscscscscscscscscscscs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcs_CZcsbcsbcsbcsbcsbcsbcsbcsbcsbcsbcsbcsbcsbcsbcsbcsbcsbcsbcsbcsbcsbcsbcsbcucucucucucucucucucucucucucucucucucucucucucucucuscuscuscuscuscuscuscuscuscuscuscuscuscuscuscuscuscuscuscuscuscuscuscvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcycycycycycycycycycycycycycycycycycycycycycycydadadadadadadadadadadadadadadadadadadadadadadadakdakdakdakdakdakdakdakdakdakdakdakdakdakdakdakdakdakdakdakdakdakdakdardardardardardardardardardardardardardardardardardardardardardardardaydaydaydaydaydaydaydaydaydaydaydaydaydaydaydaydaydaydaydaydaydaydaydededededededededededededededededededededededede_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_ATde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_CHde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEde_DEdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdeldeldeldeldeldeldeldeldeldeldeldeldeldeldeldeldeldeldeldeldeldeldeldendendendendendendendendendendendendendendendendendendendendendendendgrdgrdgrdgrdgrdgrdgrdgrdgrdgrdgrdgrdgrdgrdgrdgrdgrdgrdgrdgrdgrdgrdgrdindindindindindindindindindindindindindindindindindindindindindindindoidoidoidoidoidoidoidoidoidoidoidoidoidoidoidoidoidoidoidoidoidoidoidradradradradradradradradradradradradradradradradradradradradradradradsbdsbdsbdsbdsbdsbdsbdsbdsbdsbdsbdsbdsbdsbdsbdsbdsbdsbdsbdsbdsbdsbdsbduaduaduaduaduaduaduaduaduaduaduaduaduaduaduaduaduaduaduaduaduaduaduadumdumdumdumdumdumdumdumdumdumdumdumdumdumdumdumdumdumdumdumdumdumdumdvdvdvdvdvdvdvdvdvdvdvdvdvdvdvdvdvdvdvdvdvdvdvdyudyudyudyudyudyudyudyudyudyudyudyudyudyudyudyudyudyudyudyudyudyudyudzdzdzdzdzdzdzdzdzdzdzdzdzdzdzdzdzdzdzdzdzdzdzeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeefiefiefiefiefiefiefiefiefiefiefiefiefiefiefiefiefiefiefiefiefiefiefiegyegyegyegyegyegyegyegyegyegyegyegyegyegyegyegyegyegyegyegyegyegyegyekaekaekaekaekaekaekaekaekaekaekaekaekaekaekaekaekaekaekaekaekaekaekaelelelelelelelelelelelelelelelelelelelelelelelel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRel_GRelxelxelxelxelxelxelxelxelxelxelxelxelxelxelxelxelxelxelxelxelxelxelxenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenenen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_AUen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_CAen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_GBen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_NZen_USen_USen_USen_USen_USen_USen_USen_USen_USen_USen_USen_USen_USen_USen_USen_USen_USen_USen_USen_USen_USen_USen_USenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmeoeoeoeoeoeoeoeoeoeoeoeoeoeoeoeoeoeoeoeoeoeoeoeseseseseseseseseseseseseseseseseseseseseseseses_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_ARes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_CLes_COes_COes_COes_COes_COes_COes_COes_COes_COes_COes_COes_COes_COes_COes_COes_COes_COes_COes_COes_COes_COes_COes_COes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_CRes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_DOes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ECes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_ESes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_GTes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_HNes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_MXes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_NIes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PAes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PEes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_PRes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_SVes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_UYes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEes_VEetetetetetetetetetetetetetetetetetetetetetetetet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEet_EEeueueueueueueueueueueueueueueueueueueueueueueueu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESeu_ESewoewoewoewoewoewoewoewoewoewoewoewoewoewoewoewoewoewoewoewoewoewoewofafafafafafafafafafafafafafafafafafafafafafafafa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfa_IRfanfanfanfanfanfanfanfanfanfanfanfanfanfanfanfanfanfanfanfanfanfanfanfatfatfatfatfatfatfatfatfatfatfatfatfatfatfatfatfatfatfatfatfatfatfatfffffffffffffffffffffffffffffffffffffffffffffffifififififififififififififififififififififififi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfi_FIfilfilfilfilfilfilfilfilfilfilfilfilfilfilfilfilfilfilfilfilfilfilfilfiufiufiufiufiufiufiufiufiufiufiufiufiufiufiufiufiufiufiufiufiufiufiufjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfofofofofofofofofofofofofofofofofofofofofofofofonfonfonfonfonfonfonfonfonfonfonfonfonfonfonfonfonfonfonfonfonfonfonfrfrfrfrfrfrfrfrfrfrfrfrfrfrfrfrfrfrfrfrfrfrfrfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_BEfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CAfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_CHfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfr_FRfrmfrmfrmfrmfrmfrmfrmfrmfrmfrmfrmfrmfrmfrmfrmfrmfrmfrmfrmfrmfrmfrmfrmfrofrofrofrofrofrofrofrofrofrofrofrofrofrofrofrofrofrofrofrofrofrofrofrrfrrfrrfrrfrrfrrfrrfrrfrrfrrfrrfrrfrrfrrfrrfrrfrrfrrfrrfrrfrrfrrfrrfrsfrsfrsfrsfrsfrsfrsfrsfrsfrsfrsfrsfrsfrsfrsfrsfrsfrsfrsfrsfrsfrsfrsfurfurfurfurfurfurfurfurfurfurfurfurfurfurfurfurfurfurfurfurfurfurfurfyfyfyfyfyfyfyfyfyfyfyfyfyfyfyfyfyfyfyfyfyfyfygagagagagagagagagagagagagagagagagagagagagagagagaagaagaagaagaagaagaagaagaagaagaagaagaagaagaagaagaagaagaagaagaagaagaagaygaygaygaygaygaygaygaygaygaygaygaygaygaygaygaygaygaygaygaygaygaygaygbagbagbagbagbagbagbagbagbagbagbagbagbagbagbagbagbagbagbagbagbagbagbagdgdgdgdgdgdgdgdgdgdgdgdgdgdgdgdgdgdgdgdgdgdgdgemgemgemgemgemgemgemgemgemgemgemgemgemgemgemgemgemgemgemgemgemgemgemgezgezgezgezgezgezgezgezgezgezgezgezgezgezgezgezgezgezgezgezgezgezgezgilgilgilgilgilgilgilgilgilgilgilgilgilgilgilgilgilgilgilgilgilgilgilglglglglglglglglglglglglglglglglglglglglglglglgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgl_ESgmhgmhgmhgmhgmhgmhgmhgmhgmhgmhgmhgmhgmhgmhgmhgmhgmhgmhgmhgmhgmhgmhgmhgngngngngngngngngngngngngngngngngngngngngngngngohgohgohgohgohgohgohgohgohgohgohgohgohgohgohgohgohgohgohgohgohgohgohgongongongongongongongongongongongongongongongongongongongongongongongorgorgorgorgorgorgorgorgorgorgorgorgorgorgorgorgorgorgorgorgorgorgorgotgotgotgotgotgotgotgotgotgotgotgotgotgotgotgotgotgotgotgotgotgotgotgrbgrbgrbgrbgrbgrbgrbgrbgrbgrbgrbgrbgrbgrbgrbgrbgrbgrbgrbgrbgrbgrbgrbgrcgrcgrcgrcgrcgrcgrcgrcgrcgrcgrcgrcgrcgrcgrcgrcgrcgrcgrcgrcgrcgrcgrcgswgswgswgswgswgswgswgswgswgswgswgswgswgswgswgswgswgswgswgswgswgswgswgugugugugugugugugugugugugugugugugugugugugugugugvgvgvgvgvgvgvgvgvgvgvgvgvgvgvgvgvgvgvgvgvgvgvgwigwigwigwigwigwigwigwigwigwigwigwigwigwigwigwigwigwigwigwigwigwigwihahahahahahahahahahahahahahahahahahahahahahahahaihaihaihaihaihaihaihaihaihaihaihaihaihaihaihaihaihaihaihaihaihaihaihawhawhawhawhawhawhawhawhawhawhawhawhawhawhawhawhawhawhawhawhawhawhawhehehehehehehehehehehehehehehehehehehehehehehehe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhe_ILhihihihihihihihihihihihihihihihihihihihihihihihilhilhilhilhilhilhilhilhilhilhilhilhilhilhilhilhilhilhilhilhilhilhilhimhimhimhimhimhimhimhimhimhimhimhimhimhimhimhimhimhimhimhimhimhimhimhithithithithithithithithithithithithithithithithithithithithithithithmnhmnhmnhmnhmnhmnhmnhmnhmnhmnhmnhmnhmnhmnhmnhmnhmnhmnhmnhmnhmnhmnhmnhohohohohohohohohohohohohohohohohohohohohohohohrhrhrhrhrhrhrhrhrhrhrhrhrhrhrhrhrhrhrhrhrhrhrhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhr_HRhsbhsbhsbhsbhsbhsbhsbhsbhsbhsbhsbhsbhsbhsbhsbhsbhsbhsbhsbhsbhsbhsbhsbhthththththththththththththththththththththththuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhuhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhu_HUhuphuphuphuphuphuphuphuphuphuphuphuphuphuphuphuphuphuphuphuphuphuphuphyhyhyhyhyhyhyhyhyhyhyhyhyhyhyhyhyhyhyhyhyhyhyhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhziaiaiaiaiaiaiaiaiaiaiaiaiaiaiaiaiaiaiaiaiaiaiaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaibaidididididididididididididididididididididididieieieieieieieieieieieieieieieieieieieieieieieigigigigigigigigigigigigigigigigigigigigigigigiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijoijoijoijoijoijoijoijoijoijoijoijoijoijoijoijoijoijoijoijoijoijoijoikikikikikikikikikikikikikikikikikikikikikikikiloiloiloiloiloiloiloiloiloiloiloiloiloiloiloiloiloiloiloiloiloiloiloincincincincincincincincincincincincincincincincincincincincincincincineineineineineineineineineineineineineineineineineineineineineineineinhinhinhinhinhinhinhinhinhinhinhinhinhinhinhinhinhinhinhinhinhinhinhioioioioioioioioioioioioioioioioioioioioioioioirairairairairairairairairairairairairairairairairairairairairairairairoiroiroiroiroiroiroiroiroiroiroiroiroiroiroiroiroiroiroiroiroiroiroisisisisisisisisisisisisisisisisisisisisisisisitititititititititititititititititititititititit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_CHit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITit_ITiuiuiuiuiuiuiuiuiuiuiuiuiuiuiuiuiuiuiuiuiuiuiujajajajajajajajajajajajajajajajajajajajajajajaja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPja_JPjbojbojbojbojbojbojbojbojbojbojbojbojbojbojbojbojbojbojbojbojbojbojbojprjprjprjprjprjprjprjprjprjprjprjprjprjprjprjprjprjprjprjprjprjprjprjrbjrbjrbjrbjrbjrbjrbjrbjrbjrbjrbjrbjrbjrbjrbjrbjrbjrbjrbjrbjrbjrbjrbjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvkakakakakakakakakakakakakakakakakakakakakakakakaakaakaakaakaakaakaakaakaakaakaakaakaakaakaakaakaakaakaakaakaakaakaakabkabkabkabkabkabkabkabkabkabkabkabkabkabkabkabkabkabkabkabkabkabkabkackackackackackackackackackackackackackackackackackackackackackackackamkamkamkamkamkamkamkamkamkamkamkamkamkamkamkamkamkamkamkamkamkamkamkarkarkarkarkarkarkarkarkarkarkarkarkarkarkarkarkarkarkarkarkarkarkarkawkawkawkawkawkawkawkawkawkawkawkawkawkawkawkawkawkawkawkawkawkawkawkbdkbdkbdkbdkbdkbdkbdkbdkbdkbdkbdkbdkbdkbdkbdkbdkbdkbdkbdkbdkbdkbdkbdkgkgkgkgkgkgkgkgkgkgkgkgkgkgkgkgkgkgkgkgkgkgkgkhakhakhakhakhakhakhakhakhakhakhakhakhakhakhakhakhakhakhakhakhakhakhakhikhikhikhikhikhikhikhikhikhikhikhikhikhikhikhikhikhikhikhikhikhikhikhokhokhokhokhokhokhokhokhokhokhokhokhokhokhokhokhokhokhokhokhokhokhokikikikikikikikikikikikikikikikikikikikikikikikjkjkjkjkjkjkjkjkjkjkjkjkjkjkjkjkjkjkjkjkjkjkjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkklklklklklklklklklklklklklklklklklklklklklklklkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmbkmbkmbkmbkmbkmbkmbkmbkmbkmbkmbkmbkmbkmbkmbkmbkmbkmbkmbkmbkmbkmbkmbknknknknknknknknknknknknknknknknknknknknknknknkokokokokokokokokokokokokokokokokokokokokokokoko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRko_KRkokkokkokkokkokkokkokkokkokkokkokkokkokkokkokkokkokkokkokkokkokkokkokkoskoskoskoskoskoskoskoskoskoskoskoskoskoskoskoskoskoskoskoskoskoskoskpekpekpekpekpekpekpekpekpekpekpekpekpekpekpekpekpekpekpekpekpekpekpekrkrkrkrkrkrkrkrkrkrkrkrkrkrkrkrkrkrkrkrkrkrkrkrckrckrckrckrckrckrckrckrckrckrckrckrckrckrckrckrckrckrckrckrckrckrckrlkrlkrlkrlkrlkrlkrlkrlkrlkrlkrlkrlkrlkrlkrlkrlkrlkrlkrlkrlkrlkrlkrlkrokrokrokrokrokrokrokrokrokrokrokrokrokrokrokrokrokrokrokrokrokrokrokrukrukrukrukrukrukrukrukrukrukrukrukrukrukrukrukrukrukrukrukrukrukrukskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskskukukukukukukukukukukukukukukukukukukukukukukukumkumkumkumkumkumkumkumkumkumkumkumkumkumkumkumkumkumkumkumkumkumkumkutkutkutkutkutkutkutkutkutkutkutkutkutkutkutkutkutkutkutkutkutkutkutkvkvkvkvkvkvkvkvkvkvkvkvkvkvkvkvkvkvkvkvkvkvkvkwkwkwkwkwkwkwkwkwkwkwkwkwkwkwkwkwkwkwkwkwkwkwkykykykykykykykykykykykykykykykykykykykykykykylalalalalalalalalalalalalalalalalalalalalalalaladladladladladladladladladladladladladladladladladladladladladladladlahlahlahlahlahlahlahlahlahlahlahlahlahlahlahlahlahlahlahlahlahlahlahlamlamlamlamlamlamlamlamlamlamlamlamlamlamlamlamlamlamlamlamlamlamlamlblblblblblblblblblblblblblblblblblblblblblblblezlezlezlezlezlezlezlezlezlezlezlezlezlezlezlezlezlezlezlezlezlezlezlglglglglglglglglglglglglglglglglglglglglglglglilililililililililililililililililililililililnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlolololololololololololololololololololololololollollollollollollollollollollollollollollollollollollollollollollollozlozlozlozlozlozlozlozlozlozlozlozlozlozlozlozlozlozlozlozlozlozlozltltltltltltltltltltltltltltltltltltltltltltltlulululululululululululululululululululululululualualualualualualualualualualualualualualualualualualualualualualualuiluiluiluiluiluiluiluiluiluiluiluiluiluiluiluiluiluiluiluiluiluiluilunlunlunlunlunlunlunlunlunlunlunlunlunlunlunlunlunlunlunlunlunlunlunluoluoluoluoluoluoluoluoluoluoluoluoluoluoluoluoluoluoluoluoluoluoluoluslusluslusluslusluslusluslusluslusluslusluslusluslusluslusluslusluslvlvlvlvlvlvlvlvlvlvlvlvlvlvlvlvlvlvlvlvlvlvlvlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVlv_LVmadmadmadmadmadmadmadmadmadmadmadmadmadmadmadmadmadmadmadmadmadmadmadmagmagmagmagmagmagmagmagmagmagmagmagmagmagmagmagmagmagmagmagmagmagmagmaimaimaimaimaimaimaimaimaimaimaimaimaimaimaimaimaimaimaimaimaimaimaimakmakmakmakmakmakmakmakmakmakmakmakmakmakmakmakmakmakmakmakmakmakmakmanmanmanmanmanmanmanmanmanmanmanmanmanmanmanmanmanmanmanmanmanmanmanmapmapmapmapmapmapmapmapmapmapmapmapmapmapmapmapmapmapmapmapmapmapmapmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmdfmdfmdfmdfmdfmdfmdfmdfmdfmdfmdfmdfmdfmdfmdfmdfmdfmdfmdfmdfmdfmdfmdfmdrmdrmdrmdrmdrmdrmdrmdrmdrmdrmdrmdrmdrmdrmdrmdrmdrmdrmdrmdrmdrmdrmdrmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmenmgmgmgmgmgmgmgmgmgmgmgmgmgmgmgmgmgmgmgmgmgmgmgmgamgamgamgamgamgamgamgamgamgamgamgamgamgamgamgamgamgamgamgamgamgamgamhmhmhmhmhmhmhmhmhmhmhmhmhmhmhmhmhmhmhmhmhmhmhmimimimimimimimimimimimimimimimimimimimimimimimicmicmicmicmicmicmicmicmicmicmicmicmicmicmicmicmicmicmicmicmicmicmicminminminminminminminminminminminminminminminminminminminminminminminmismismismismismismismismismismismismismismismismismismismismismismismkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkhmkhmkhmkhmkhmkhmkhmkhmkhmkhmkhmkhmkhmkhmkhmkhmkhmkhmkhmkhmkhmkhmkhmlmlmlmlmlmlmlmlmlmlmlmlmlmlmlmlmlmlmlmlmlmlmlmnmnmnmnmnmnmnmnmnmnmnmnmnmnmnmnmnmnmnmnmnmnmnmncmncmncmncmncmncmncmncmncmncmncmncmncmncmncmncmncmncmncmncmncmncmncmnimnimnimnimnimnimnimnimnimnimnimnimnimnimnimnimnimnimnimnimnimnimnimnomnomnomnomnomnomnomnomnomnomnomnomnomnomnomnomnomnomnomnomnomnomnomomomomomomomomomomomomomomomomomomomomomomomomohmohmohmohmohmohmohmohmohmohmohmohmohmohmohmohmohmohmohmohmohmohmohmosmosmosmosmosmosmosmosmosmosmosmosmosmosmosmosmosmosmosmosmosmosmosmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYms_MYmtmtmtmtmtmtmtmtmtmtmtmtmtmtmtmtmtmtmtmtmtmtmtmulmulmulmulmulmulmulmulmulmulmulmulmulmulmulmulmulmulmulmulmulmulmulmunmunmunmunmunmunmunmunmunmunmunmunmunmunmunmunmunmunmunmunmunmunmunmusmusmusmusmusmusmusmusmusmusmusmusmusmusmusmusmusmusmusmusmusmusmusmwlmwlmwlmwlmwlmwlmwlmwlmwlmwlmwlmwlmwlmwlmwlmwlmwlmwlmwlmwlmwlmwlmwlmwrmwrmwrmwrmwrmwrmwrmwrmwrmwrmwrmwrmwrmwrmwrmwrmwrmwrmwrmwrmwrmwrmwrmymymymymymymymymymymymymymymymymymymymymymymymy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmy_MMmynmynmynmynmynmynmynmynmynmynmynmynmynmynmynmynmynmynmynmynmynmynmynmyvmyvmyvmyvmyvmyvmyvmyvmyvmyvmyvmyvmyvmyvmyvmyvmyvmyvmyvmyvmyvmyvmyvnanananananananananananananananananananananananahnahnahnahnahnahnahnahnahnahnahnahnahnahnahnahnahnahnahnahnahnahnahnainainainainainainainainainainainainainainainainainainainainainainainapnapnapnapnapnapnapnapnapnapnapnapnapnapnapnapnapnapnapnapnapnapnapnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnbnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOnb_NOndndndndndndndndndndndndndndndndndndndndndndndndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsndsnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnds_DEnenenenenenenenenenenenenenenenenenenenenenenenewnewnewnewnewnewnewnewnewnewnewnewnewnewnewnewnewnewnewnewnewnewnewngngngngngngngngngngngngngngngngngngngngngngngnianianianianianianianianianianianianianianianianianianianianianianianicnicnicnicnicnicnicnicnicnicnicnicnicnicnicnicnicnicnicnicnicnicnicniuniuniuniuniuniuniuniuniuniuniuniuniuniuniuniuniuniuniuniuniuniuniunlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnlnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_BEnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnl_NLnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnonononononononononononononononononononononononognognognognognognognognognognognognognognognognognognognognognognognonnonnonnonnonnonnonnonnonnonnonnonnonnonnonnonnonnonnonnonnonnonnonnqonqonqonqonqonqonqonqonqonqonqonqonqonqonqonqonqonqonqonqonqonqonqonrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonubnubnubnubnubnubnubnubnubnubnubnubnubnubnubnubnubnubnubnubnubnubnubnvnvnvnvnvnvnvnvnvnvnvnvnvnvnvnvnvnvnvnvnvnvnvnwcnwcnwcnwcnwcnwcnwcnwcnwcnwcnwcnwcnwcnwcnwcnwcnwcnwcnwcnwcnwcnwcnwcnynynynynynynynynynynynynynynynynynynynynynynynymnymnymnymnymnymnymnymnymnymnymnymnymnymnymnymnymnymnymnymnymnymnymnynnynnynnynnynnynnynnynnynnynnynnynnynnynnynnynnynnynnynnynnynnynnynnyonyonyonyonyonyonyonyonyonyonyonyonyonyonyonyonyonyonyonyonyonyonyonzinzinzinzinzinzinzinzinzinzinzinzinzinzinzinzinzinzinzinzinzinzinziocococococococococococococococococococococococojojojojojojojojojojojojojojojojojojojojojojojomomomomomomomomomomomomomomomomomomomomomomomorororororororororororororororororororororororososososososososososososososososososososososososaosaosaosaosaosaosaosaosaosaosaosaosaosaosaosaosaosaosaosaosaosaosaotaotaotaotaotaotaotaotaotaotaotaotaotaotaotaotaotaotaotaotaotaotaotaotootootootootootootootootootootootootootootootootootootootootootootopapapapapapapapapapapapapapapapapapapapapapapapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapaapagpagpagpagpagpagpagpagpagpagpagpagpagpagpagpagpagpagpagpagpagpagpagpalpalpalpalpalpalpalpalpalpalpalpalpalpalpalpalpalpalpalpalpalpalpalpampampampampampampampampampampampampampampampampampampampampampampampappappappappappappappappappappappappappappappappappappappappappappappaupaupaupaupaupaupaupaupaupaupaupaupaupaupaupaupaupaupaupaupaupaupaupeopeopeopeopeopeopeopeopeopeopeopeopeopeopeopeopeopeopeopeopeopeopeophiphiphiphiphiphiphiphiphiphiphiphiphiphiphiphiphiphiphiphiphiphiphiphnphnphnphnphnphnphnphnphnphnphnphnphnphnphnphnphnphnphnphnphnphnphnpipipipipipipipipipipipipipipipipipipipipipipiplplplplplplplplplplplplplplplplplplplplplplplpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLpl_PLponponponponponponponponponponponponponponponponponponponponponponponprapraprapraprapraprapraprapraprapraprapraprapraprapraprapraprapraprapropropropropropropropropropropropropropropropropropropropropropropropspspspspspspspspspspspspspspspspspspspspspspsptptptptptptptptptptptptptptptptptptptptptptptpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_BRpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTpt_PTqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzqaa-qtzquququququququququququququququququququququququrajrajrajrajrajrajrajrajrajrajrajrajrajrajrajrajrajrajrajrajrajrajrajraprapraprapraprapraprapraprapraprapraprapraprapraprapraprapraprapraprarrarrarrarrarrarrarrarrarrarrarrarrarrarrarrarrarrarrarrarrarrarrarrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrmrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrorororororororororororororororororororororororoaroaroaroaroaroaroaroaroaroaroaroaroaroaroaroaroaroaroaroaroaroaroaromromromromromromromromromromromromromromromromromromromromromromromrurururururururururururururururururururururururu_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUru_RUruprupruprupruprupruprupruprupruprupruprupruprupruprupruprupruprupruprwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwsasasasasasasasasasasasasasasasasasasasasasasasadsadsadsadsadsadsadsadsadsadsadsadsadsadsadsadsadsadsadsadsadsadsadsahsahsahsahsahsahsahsahsahsahsahsahsahsahsahsahsahsahsahsahsahsahsahsaisaisaisaisaisaisaisaisaisaisaisaisaisaisaisaisaisaisaisaisaisaisaisalsalsalsalsalsalsalsalsalsalsalsalsalsalsalsalsalsalsalsalsalsalsalsamsamsamsamsamsamsamsamsamsamsamsamsamsamsamsamsamsamsamsamsamsamsamsassassassassassassassassassassassassassassassassassassassassassassassatsatsatsatsatsatsatsatsatsatsatsatsatsatsatsatsatsatsatsatsatsatsatscscscscscscscscscscscscscscscscscscscscscscscscnscnscnscnscnscnscnscnscnscnscnscnscnscnscnscnscnscnscnscnscnscnscnscoscoscoscoscoscoscoscoscoscoscoscoscoscoscoscoscoscoscoscoscoscoscosdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdseseseseseseseseseseseseseseseseseseseseseseseselselselselselselselselselselselselselselselselselselselselselselselsemsemsemsemsemsemsemsemsemsemsemsemsemsemsemsemsemsemsemsemsemsemsemsgsgsgsgsgsgsgsgsgsgsgsgsgsgsgsgsgsgsgsgsgsgsgsgasgasgasgasgasgasgasgasgasgasgasgasgasgasgasgasgasgasgasgasgasgasgasgnsgnsgnsgnsgnsgnsgnsgnsgnsgnsgnsgnsgnsgnsgnsgnsgnsgnsgnsgnsgnsgnsgnshnshnshnshnshnshnshnshnshnshnshnshnshnshnshnshnshnshnshnshnshnshnshnsisisisisisisisisisisisisisisisisisisisisisisisidsidsidsidsidsidsidsidsidsidsidsidsidsidsidsidsidsidsidsidsidsidsidsiosiosiosiosiosiosiosiosiosiosiosiosiosiosiosiosiosiosiosiosiosiosiositsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitskskskskskskskskskskskskskskskskskskskskskskskslslslslslslslslslslslslslslslslslslslslslslslsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIsl_SIslaslaslaslaslaslaslaslaslaslaslaslaslaslaslaslaslaslaslaslaslaslaslasmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmsmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmasmismismismismismismismismismismismismismismismismismismismismismismismjsmjsmjsmjsmjsmjsmjsmjsmjsmjsmjsmjsmjsmjsmjsmjsmjsmjsmjsmjsmjsmjsmjsmnsmnsmnsmnsmnsmnsmnsmnsmnsmnsmnsmnsmnsmnsmnsmnsmnsmnsmnsmnsmnsmnsmnsmssmssmssmssmssmssmssmssmssmssmssmssmssmssmssmssmssmssmssmssmssmssmssnsnsnsnsnsnsnsnsnsnsnsnsnsnsnsnsnsnsnsnsnsnsnsnksnksnksnksnksnksnksnksnksnksnksnksnksnksnksnksnksnksnksnksnksnksnksosososososososososososososososososososososososogsogsogsogsogsogsogsogsogsogsogsogsogsogsogsogsogsogsogsogsogsogsogsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsqsqsqsqsqsqsqsqsqsqsqsqsqsqsqsqsqsqsqsqsqsqsqsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsq_ALsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsrsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsr_RSsrnsrnsrnsrnsrnsrnsrnsrnsrnsrnsrnsrnsrnsrnsrnsrnsrnsrnsrnsrnsrnsrnsrnsrrsrrsrrsrrsrrsrrsrrsrrsrrsrrsrrsrrsrrsrrsrrsrrsrrsrrsrrsrrsrrsrrsrrssssssssssssssssssssssssssssssssssssssssssssssssassassassassassassassassassassassassassassassassassassassassassassastststststststststststststststststststststststsusususususususususususususususususususususususuksuksuksuksuksuksuksuksuksuksuksuksuksuksuksuksuksuksuksuksuksuksuksussussussussussussussussussussussussussussussussussussussussussussussuxsuxsuxsuxsuxsuxsuxsuxsuxsuxsuxsuxsuxsuxsuxsuxsuxsuxsuxsuxsuxsuxsuxsvsvsvsvsvsvsvsvsvsvsvsvsvsvsvsvsvsvsvsvsvsvsvsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEsv_SEswswswswswswswswswswswswswswswswswswswswswswswsycsycsycsycsycsycsycsycsycsycsycsycsycsycsycsycsycsycsycsycsycsycsycsyrsyrsyrsyrsyrsyrsyrsyrsyrsyrsyrsyrsyrsyrsyrsyrsyrsyrsyrsyrsyrsyrsyrtatatatatatatatatatatatatatatatatatatatatatatataitaitaitaitaitaitaitaitaitaitaitaitaitaitaitaitaitaitaitaitaitaitaitetetetetetetetetetetetetetetetetetetetetetetetemtemtemtemtemtemtemtemtemtemtemtemtemtemtemtemtemtemtemtemtemtemtemtertertertertertertertertertertertertertertertertertertertertertertertettettettettettettettettettettettettettettettettettettettettettettettgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgthththththththththththththththththththththththtitititititititititititititititititititititititigtigtigtigtigtigtigtigtigtigtigtigtigtigtigtigtigtigtigtigtigtigtigtivtivtivtivtivtivtivtivtivtivtivtivtivtivtivtivtivtivtivtivtivtivtivtktktktktktktktktktktktktktktktktktktktktktktktkltkltkltkltkltkltkltkltkltkltkltkltkltkltkltkltkltkltkltkltkltkltkltltltltltltltltltltltltltltltltltltltltltltltltlhtlhtlhtlhtlhtlhtlhtlhtlhtlhtlhtlhtlhtlhtlhtlhtlhtlhtlhtlhtlhtlhtlhtlitlitlitlitlitlitlitlitlitlitlitlitlitlitlitlitlitlitlitlitlitlitlitmhtmhtmhtmhtmhtmhtmhtmhtmhtmhtmhtmhtmhtmhtmhtmhtmhtmhtmhtmhtmhtmhtmhtntntntntntntntntntntntntntntntntntntntntntntntotototototototototototototototototototototototogtogtogtogtogtogtogtogtogtogtogtogtogtogtogtogtogtogtogtogtogtogtogtpitpitpitpitpitpitpitpitpitpitpitpitpitpitpitpitpitpitpitpitpitpitpitrtrtrtrtrtrtrtrtrtrtrtrtrtrtrtrtrtrtrtrtrtrtrtstststststststststststststststststststststststsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitsitttttttttttttttttttttttttttttttttttttttttttttttumtumtumtumtumtumtumtumtumtumtumtumtumtumtumtumtumtumtumtumtumtumtumtuptuptuptuptuptuptuptuptuptuptuptuptuptuptuptuptuptuptuptuptuptuptuptuttuttuttuttuttuttuttuttuttuttuttuttuttuttuttuttuttuttuttuttuttuttuttvltvltvltvltvltvltvltvltvltvltvltvltvltvltvltvltvltvltvltvltvltvltvltwtwtwtwtwtwtwtwtwtwtwtwtwtwtwtwtwtwtwtwtwtwtwtytytytytytytytytytytytytytytytytytytytytytytytyvtyvtyvtyvtyvtyvtyvtyvtyvtyvtyvtyvtyvtyvtyvtyvtyvtyvtyvtyvtyvtyvtyvudmudmudmudmudmudmudmudmudmudmudmudmudmudmudmudmudmudmudmudmudmudmudmugugugugugugugugugugugugugugugugugugugugugugugugaugaugaugaugaugaugaugaugaugaugaugaugaugaugaugaugaugaugaugaugaugaugaukukukukukukukukukukukukukukukukukukukukukukukuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAuk_UAumbumbumbumbumbumbumbumbumbumbumbumbumbumbumbumbumbumbumbumbumbumbumbundundundundundundundundundundundundundundundundundundundundundundundurururururururururururururururururururururururur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKur_PKuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzvaivaivaivaivaivaivaivaivaivaivaivaivaivaivaivaivaivaivaivaivaivaivaivevevevevevevevevevevevevevevevevevevevevevevevivivivivivivivivivivivivivivivivivivivivivivivovovovovovovovovovovovovovovovovovovovovovovovotvotvotvotvotvotvotvotvotvotvotvotvotvotvotvotvotvotvotvotvotvotvotwawawawawawawawawawawawawawawawawawawawawawawawakwakwakwakwakwakwakwakwakwakwakwakwakwakwakwakwakwakwakwakwakwakwakwalwalwalwalwalwalwalwalwalwalwalwalwalwalwalwalwalwalwalwalwalwalwalwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwarwaswaswaswaswaswaswaswaswaswaswaswaswaswaswaswaswaswaswaswaswaswaswaswenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwenwowowowowowowowowowowowowowowowowowowowowowowoxalxalxalxalxalxalxalxalxalxalxalxalxalxalxalxalxalxalxalxalxalxalxalxhxhxhxhxhxhxhxhxhxhxhxhxhxhxhxhxhxhxhxhxhxhxhyaoyaoyaoyaoyaoyaoyaoyaoyaoyaoyaoyaoyaoyaoyaoyaoyaoyaoyaoyaoyaoyaoyaoyapyapyapyapyapyapyapyapyapyapyapyapyapyapyapyapyapyapyapyapyapyapyapyiyiyiyiyiyiyiyiyiyiyiyiyiyiyiyiyiyiyiyiyiyiyiyoyoyoyoyoyoyoyoyoyoyoyoyoyoyoyoyoyoyoyoyoyoyoypkypkypkypkypkypkypkypkypkypkypkypkypkypkypkypkypkypkypkypkypkypkypkzazazazazazazazazazazazazazazazazazazazazazazazapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzapzblzblzblzblzblzblzblzblzblzblzblzblzblzblzblzblzblzblzblzblzblzblzblzenzenzenzenzenzenzenzenzenzenzenzenzenzenzenzenzenzenzenzenzenzenzenzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzhzh_CNzh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CN.GB2312zh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_CNzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_HKzh_TWzh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TW.Big5zh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzh_TWzndzndzndzndzndzndzndzndzndzndzndzndzndzndzndzndzndzndzndzndzndzndzndzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzuzunzunzunzunzunzunzunzunzunzunzunzunzunzunzunzunzunzunzunzunzunzunzunzxxzxxzxxzxxzxxzxxzxxzxxzxxzxxzxxzxxzxxzxxzxxzxxzxxzxxzxxzxxzxxzxxzxxzzazzazzazzazzazzazzazzazzazzazzazzazzazzazzazzazzazzazzazzazzazzazza2.4.44-1.fc162.4.44-1.fc16      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMN OPPPPPPPPPPPPPPPPPPPPPPOQQQQQQQQQQQQQQQQQQQQQQORRRRRRRRRRRRRRRRRRRRRROSSSSSSSSSSSSSSSSSSSSSSOTTTTTTTTTTTTTTTTTTTTTTOUUUUUUUUUUUUUUUUUUUUUUOVVVVVVVVVVVVVVVVVVVVVVOWWWWWWWWWWWWWWWWWWWWWWOXXXXXXXXXXXXXXXXXXXXXXOYYYYYYYYYYYYYYYYYYYYYYOZZZZZZZZZZZZZZZZZZZZZZO[[[[[[[[[[[[[[[[[[[[[[O\\\\\\\\\\\\\\\\\\\\\\O]]]]]]]]]]]]]]]]]]]]]]O^^^^^^^^^^^^^^^^^^^^^^O______________________O``````````````````````OaaaaaaaaaaaaaaaaaaaaaaObbbbbbbbbbbbbbbbbbbbbbOccccccccccccccccccccccOddddddddddddddddddddddOeeeeeeeeeeeeeeeeeeeeeeOffffffffffffffffffffffOggggggggggggggggggggggOhhhhhhhhhhhhhhhhhhhhhhOiiiiiiiiiiiiiiiiiiiiiiOjjjjjjjjjjjjjjjjjjjjjjOkkkkkkkkkkkkkkkkkkkkkkOllllllllllllllllllllllOmmmmmmmmmmmmmmmmmmmmmmOnnnnnnnnnnnnnnnnnnnnnnOooooooooooooooooooooooOppppppppppppppppppppppOqqqqqqqqqqqqqqqqqqqqqqOrrrrrrrrrrrrrrrrrrrrrrOssssssssssssssssssssssOttttttttttttttttttttttOuuuuuuuuuuuuuuuuuuuuuuOvvvvvvvvvvvvvvvvvvvvvvOwwwwwwwwwwwwwwwwwwwwwwOxxxxxxxxxxxxxxxxxxxxxxOyyyyyyyyyyyyyyyyyyyyyyOzzzzzzzzzzzzzzzzzzzzzzO{{{{{{{{{{{{{{{{{{{{{{O||||||||||||||||||||||O}}}}}}}}}}}}}}}}}}}}}}O~~~~~~~~~~~~~~~~~~~~~~OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO                      O                      O                      O                      O                      OOOOOOOOOOOOOOOOOOO                      O!!!!!!!!!!!!!!!!!!!!!!O""""""""""""""""""""""O######################O$$$$$$$$$$$$$$$$$$$$$$O%%%%%%%%%%%%%%%%%%%%%%O&&&&&&&&&&&&&&&&&&&&&&O''''''''''''''''''''''O((((((((((((((((((((((O))))))))))))))))))))))O**********************O++++++++++++++++++++++O,,,,,,,,,,,,,,,,,,,,,,O----------------------O......................O//////////////////////O0000000000000000000000O1111111111111111111111O2222222222222222222222O3333333333333333333333O4444444444444444444444O5555555555555555555555O6666666666666666666666O7777777777777777777777O8888888888888888888888O9999999999999999999999O::::::::::::::::::::::O;;;;;;;;;;;;;;;;;;;;;;O<<<<<<<<<<<<<<<<<<<<<<O======================O>>>>>>>>>>>>>>>>>>>>>>O??????????????????????O@@@@@@@@@@@@@@@@@@@@@@OAAAAAAAAAAAAAAAAAAAAAAOBBBBBBBBBBBBBBBBBBBBBBOCCCCCCCCCCCCCCCCCCCCCCODDDDDDDDDDDDDDDDDDDDDDOEEEEEEEEEEEEEEEEEEEEEEOFFFFFFFFFFFFFFFFFFFFFFOGGGGGGGGGGGGGGGGGGGGGGOHHHHHHHHHHHHHHHHHHHHHHOIIIIIIIIIIIIIIIIIIIIIIOJJJJJJJJJJJJJJJJJJJJJJOKKKKKKKKKKKKKKKKKKKKKKOLLLLLLLLLLLLLLLLLLLLLLOMMMMMMMMMMMMMMMMMMMMMMONNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPOQQQQQQQQQQQQQQQQQQQQQQORRRRRRRRRRRRRRRRRRRRRROSSSSSSSSSSSSSSSSSSSSSSOTTTTTTTTTTTTTTTTTTTTTTOUUUUUUUUUUUUUUUUUUUUUUOVVVVVVVVVVVVVVVVVVVVVVOWWWWWWWWWWWWWWWWWWWWWWOXXXXXXXXXXXXXXXXXXXXXXOYYYYYYYYYYYYYYYYYYYYYYOZZZZZZZZZZZZZZZZZZZZZZO[[[[[[[[[[[[[[[[[[[[[[O\\\\\\\\\\\\\\\\\\\\\\O]]]]]]]]]]]]]]]]]]]]]]O^^^^^^^^^^^^^^^^^^^^^^O______________________O``````````````````````OaaaaaaaaaaaaaaaaaaaaaaObbbbbbbbbbbbbbbbbbbbbbOccccccccccccccccccccccOddddddddddddddddddddddOeeeeeeeeeeeeeeeeeeeeeeOffffffffffffffffffffffOggggggggggggggggggggggOhhhhhhhhhhhhhhhhhhhhhhOiiiiiiiiiiiiiiiiiiiiiiOjjjjjjjjjjjjjjjjjjjjjjOkkkkkkkkkkkkkkkkkkkkkkOllllllllllllllllllllllOmmmmmmmmmmmmmmmmmmmmmmOnnnnnnnnnnnnnnnnnnnnnnOooooooooooooooooooooooOppppppppppppppppppppppOqqqqqqqqqqqqqqqqqqqqqqOrrrrrrrrrrrrrrrrrrrrrrOssssssssssssssssssssssOttttttttttttttttttttttOuuuuuuuuuuuuuuuuuuuuuuOvvvvvvvvvvvvvvvvvvvvvvOwwwwwwwwwwwwwwwwwwwwwwOxxxxxxxxxxxxxxxxxxxxxxOyyyyyyyyyyyyyyyyyyyyyyOzzzzzzzzzzzzzzzzzzzzzzO{{{{{{{{{{{{{{{{{{{{{{O||||||||||||||||||||||O}}}}}}}}}}}}}}}}}}}}}}O~~~~~~~~~~~~~~~~~~~~~~OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO                      O                      O                      O                      O                      OOOOOOOOOOOOOOOOOOO                      O!!!!!!!!!!!!!!!!!!!!!!O""""""""""""""""""""""O######################O$$$$$$$$$$$$$$$$$$$$$$O%%%%%%%%%%%%%%%%%%%%%%O&&&&&&&&&&&&&&&&&&&&&&O''''''''''''''''''''''O((((((((((((((((((((((O))))))))))))))))))))))O**********************O++++++++++++++++++++++O,,,,,,,,,,,,,,,,,,,,,,O----------------------O......................O//////////////////////O0000000000000000000000O1111111111111111111111O2222222222222222222222O3333333333333333333333O4444444444444444444444O5555555555555555555555O6666666666666666666666O7777777777777777777777O8888888888888888888888O9999999999999999999999O::::::::::::::::::::::O;;;;;;;;;;;;;;;;;;;;;;O<<<<<<<<<<<<<<<<<<<<<<O======================O>>>>>>>>>>>>>>>>>>>>>>O??????????????????????O@@@@@@@@@@@@@@@@@@@@@@OAAAAAAAAAAAAAAAAAAAAAAOBBBBBBBBBBBBBBBBBBBBBBOCCCCCCCCCCCCCCCCCCCCCCODDDDDDDDDDDDDDDDDDDDDDOEEEEEEEEEEEEEEEEEEEEEEOFFFFFFFFFFFFFFFFFFFFFFOGGGGGGGGGGGGGGGGGGGGGGOHHHHHHHHHHHHHHHHHHHHHHOIIIIIIIIIIIIIIIIIIIIIIOJJJJJJJJJJJJJJJJJJJJJJOKKKKKKKKKKKKKKKKKKKKKKOLLLLLLLLLLLLLLLLLLLLLLOMMMMMMMMMMMMMMMMMMMMMMONNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPOQQQQQQQQQQQQQQQQQQQQQQORRRRRRRRRRRRRRRRRRRRRROSSSSSSSSSSSSSSSSSSSSSSOTTTTTTTTTTTTTTTTTTTTTTOUUUUUUUUUUUUUUUUUUUUUUOVVVVVVVVVVVVVVVVVVVVVVOWWWWWWWWWWWWWWWWWWWWWWOXXXXXXXXXXXXXXXXXXXXXXOYYYYYYYYYYYYYYYYYYYYYYOZZZZZZZZZZZZZZZZZZZZZZO[[[[[[[[[[[[[[[[[[[[[[O\\\\\\\\\\\\\\\\\\\\\\O]]]]]]]]]]]]]]]]]]]]]]O^^^^^^^^^^^^^^^^^^^^^^O______________________O``````````````````````OaaaaaaaaaaaaaaaaaaaaaaObbbbbbbbbbbbbbbbbbbbbbOccccccccccccccccccccccOddddddddddddddddddddddOeeeeeeeeeeeeeeeeeeeeeeOffffffffffffffffffffffOggggggggggggggggggggggOhhhhhhhhhhhhhhhhhhhhhhOiiiiiiiiiiiiiiiiiiiiiiOjjjjjjjjjjjjjjjjjjjjjjOkkkkkkkkkkkkkkkkkkkkkkOllllllllllllllllllllllOmmmmmmmmmmmmmmmmmmmmmmOnnnnnnnnnnnnnnnnnnnnnnOooooooooooooooooooooooOppppppppppppppppppppppOqqqqqqqqqqqqqqqqqqqqqqOrrrrrrrrrrrrrrrrrrrrrrOssssssssssssssssssssssOttttttttttttttttttttttOuuuuuuuuuuuuuuuuuuuuuuOvvvvvvvvvvvvvvvvvvvvvvOwwwwwwwwwwwwwwwwwwwwwwOxxxxxxxxxxxxxxxxxxxxxxOyyyyyyyyyyyyyyyyyyyyyyOzzzzzzzzzzzzzzzzzzzzzzO{{{{{{{{{{{{{{{{{{{{{{O||||||||||||||||||||||O}}}}}}}}}}}}}}}}}}}}}}O~~~~~~~~~~~~~~~~~~~~~~OOOOOOOOOOOOOOO binbootdevetcX11applnkfontpath.doptpkipmconfig.dpower.dsleep.dskelsysconfigxdgautostartxinetd.dhomelibmodulestlsmediamntoptprocrootrunlocksbinsrvsystmpusrbinetcgamesincludelibX11gameslocalepm-utilsmodule.dpower.dsleep.dsse2tlslibexeclocalbinetcgamesincludeliblibexecsbinshareapplicationsinfomanman1man1xman2man2xman3man3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsrcsbinshareX11aclocalapplicationsaugeaslensesbackgroundsdesktop-directoriesdictdocemptygamesghostscriptconf.dgnomeiconsidlinfolocaleaaLC_MESSAGESabLC_MESSAGESaceLC_MESSAGESachLC_MESSAGESadaLC_MESSAGESadyLC_MESSAGESaeLC_MESSAGESafLC_MESSAGESaf_ZALC_MESSAGESafaLC_MESSAGESafhLC_MESSAGESainLC_MESSAGESakLC_MESSAGESakkLC_MESSAGESaleLC_MESSAGESalgLC_MESSAGESaltLC_MESSAGESamLC_MESSAGESam_ETLC_MESSAGESanLC_MESSAGESangLC_MESSAGESanpLC_MESSAGESapaLC_MESSAGESarLC_MESSAGESarcLC_MESSAGESarnLC_MESSAGESarpLC_MESSAGESartLC_MESSAGESarwLC_MESSAGESasLC_MESSAGESastLC_MESSAGESast_ESLC_MESSAGESathLC_MESSAGESausLC_MESSAGESavLC_MESSAGESawaLC_MESSAGESayLC_MESSAGESazLC_MESSAGESaz_IRLC_MESSAGESbaLC_MESSAGESbadLC_MESSAGESbaiLC_MESSAGESbalLC_MESSAGESbanLC_MESSAGESbasLC_MESSAGESbatLC_MESSAGESbeLC_MESSAGESbe@latinLC_MESSAGESbejLC_MESSAGESbemLC_MESSAGESberLC_MESSAGESbgLC_MESSAGESbg_BGLC_MESSAGESbhLC_MESSAGESbhoLC_MESSAGESbiLC_MESSAGESbikLC_MESSAGESbinLC_MESSAGESblaLC_MESSAGESbmLC_MESSAGESbnLC_MESSAGESbn_INLC_MESSAGESbntLC_MESSAGESboLC_MESSAGESbrLC_MESSAGESbraLC_MESSAGESbsLC_MESSAGESbtkLC_MESSAGESbuaLC_MESSAGESbugLC_MESSAGESbynLC_MESSAGEScaLC_MESSAGESca@valenciaLC_MESSAGESca_ESLC_MESSAGESca_ES@valencianLC_MESSAGEScadLC_MESSAGEScaiLC_MESSAGEScarLC_MESSAGEScauLC_MESSAGESceLC_MESSAGEScebLC_MESSAGEScelLC_MESSAGESchLC_MESSAGESchbLC_MESSAGESchgLC_MESSAGESchkLC_MESSAGESchmLC_MESSAGESchnLC_MESSAGESchoLC_MESSAGESchpLC_MESSAGESchrLC_MESSAGESchyLC_MESSAGEScmcLC_MESSAGEScoLC_MESSAGEScopLC_MESSAGEScpeLC_MESSAGEScpfLC_MESSAGEScppLC_MESSAGEScrLC_MESSAGEScrhLC_MESSAGEScrpLC_MESSAGEScsLC_MESSAGEScs_CZLC_MESSAGEScsbLC_MESSAGEScuLC_MESSAGEScusLC_MESSAGEScvLC_MESSAGEScyLC_MESSAGESdaLC_MESSAGESdakLC_MESSAGESdarLC_MESSAGESdayLC_MESSAGESdeLC_MESSAGESde_ATLC_MESSAGESde_CHLC_MESSAGESde_DELC_MESSAGESdefaultLC_MESSAGESdelLC_MESSAGESdenLC_MESSAGESdgrLC_MESSAGESdinLC_MESSAGESdoiLC_MESSAGESdraLC_MESSAGESdsbLC_MESSAGESduaLC_MESSAGESdumLC_MESSAGESdvLC_MESSAGESdyuLC_MESSAGESdzLC_MESSAGESeeLC_MESSAGESefiLC_MESSAGESegyLC_MESSAGESekaLC_MESSAGESelLC_MESSAGESel_GRLC_MESSAGESelxLC_MESSAGESenLC_MESSAGESen@boldquotLC_MESSAGESen@quotLC_MESSAGESen@shawLC_MESSAGESen_AULC_MESSAGESen_CALC_MESSAGESen_GBLC_MESSAGESen_NZLC_MESSAGESen_USLC_MESSAGESenmLC_MESSAGESeoLC_MESSAGESesLC_MESSAGESes_ARLC_MESSAGESes_CLLC_MESSAGESes_COLC_MESSAGESes_CRLC_MESSAGESes_DOLC_MESSAGESes_ECLC_MESSAGESes_ESLC_MESSAGESes_GTLC_MESSAGESes_HNLC_MESSAGESes_MXLC_MESSAGESes_NILC_MESSAGESes_PALC_MESSAGESes_PELC_MESSAGESes_PRLC_MESSAGESes_SVLC_MESSAGESes_UYLC_MESSAGESes_VELC_MESSAGESetLC_MESSAGESet_EELC_MESSAGESeuLC_MESSAGESeu_ESLC_MESSAGESewoLC_MESSAGESfaLC_MESSAGESfa_IRLC_MESSAGESfanLC_MESSAGESfatLC_MESSAGESffLC_MESSAGESfiLC_MESSAGESfi_FILC_MESSAGESfilLC_MESSAGESfiuLC_MESSAGESfjLC_MESSAGESfoLC_MESSAGESfonLC_MESSAGESfrLC_MESSAGESfr_BELC_MESSAGESfr_CALC_MESSAGESfr_CHLC_MESSAGESfr_FRLC_MESSAGESfrmLC_MESSAGESfroLC_MESSAGESfrrLC_MESSAGESfrsLC_MESSAGESfurLC_MESSAGESfyLC_MESSAGESgaLC_MESSAGESgaaLC_MESSAGESgayLC_MESSAGESgbaLC_MESSAGESgdLC_MESSAGESgemLC_MESSAGESgezLC_MESSAGESgilLC_MESSAGESglLC_MESSAGESgl_ESLC_MESSAGESgmhLC_MESSAGESgnLC_MESSAGESgohLC_MESSAGESgonLC_MESSAGESgorLC_MESSAGESgotLC_MESSAGESgrbLC_MESSAGESgrcLC_MESSAGESgswLC_MESSAGESguLC_MESSAGESgvLC_MESSAGESgwiLC_MESSAGEShaLC_MESSAGEShaiLC_MESSAGEShawLC_MESSAGESheLC_MESSAGEShe_ILLC_MESSAGEShiLC_MESSAGEShilLC_MESSAGEShimLC_MESSAGEShitLC_MESSAGEShmnLC_MESSAGEShoLC_MESSAGEShrLC_MESSAGEShr_HRLC_MESSAGEShsbLC_MESSAGEShtLC_MESSAGEShuLC_MESSAGEShu_HULC_MESSAGEShupLC_MESSAGEShyLC_MESSAGEShzLC_MESSAGESiaLC_MESSAGESibaLC_MESSAGESidLC_MESSAGESieLC_MESSAGESigLC_MESSAGESiiLC_MESSAGESijoLC_MESSAGESikLC_MESSAGESiloLC_MESSAGESincLC_MESSAGESineLC_MESSAGESinhLC_MESSAGESioLC_MESSAGESiraLC_MESSAGESiroLC_MESSAGESisLC_MESSAGESitLC_MESSAGESit_CHLC_MESSAGESit_ITLC_MESSAGESiuLC_MESSAGESjaLC_MESSAGESja_JPLC_MESSAGESjboLC_MESSAGESjprLC_MESSAGESjrbLC_MESSAGESjvLC_MESSAGESkaLC_MESSAGESkaaLC_MESSAGESkabLC_MESSAGESkacLC_MESSAGESkamLC_MESSAGESkarLC_MESSAGESkawLC_MESSAGESkbdLC_MESSAGESkgLC_MESSAGESkhaLC_MESSAGESkhiLC_MESSAGESkhoLC_MESSAGESkiLC_MESSAGESkjLC_MESSAGESkkLC_MESSAGESklLC_MESSAGESkmLC_MESSAGESkmbLC_MESSAGESknLC_MESSAGESkoLC_MESSAGESko_KRLC_MESSAGESkokLC_MESSAGESkosLC_MESSAGESkpeLC_MESSAGESkrLC_MESSAGESkrcLC_MESSAGESkrlLC_MESSAGESkroLC_MESSAGESkruLC_MESSAGESksLC_MESSAGESks@devanagariLC_MESSAGESkuLC_MESSAGESkumLC_MESSAGESkutLC_MESSAGESkvLC_MESSAGESkwLC_MESSAGESkyLC_MESSAGESlaLC_MESSAGESladLC_MESSAGESlahLC_MESSAGESlamLC_MESSAGESlbLC_MESSAGESlezLC_MESSAGESlgLC_MESSAGESliLC_MESSAGESlnLC_MESSAGESloLC_MESSAGESlolLC_MESSAGESlozLC_MESSAGESltLC_MESSAGESluLC_MESSAGESluaLC_MESSAGESluiLC_MESSAGESlunLC_MESSAGESluoLC_MESSAGESlusLC_MESSAGESlvLC_MESSAGESlv_LVLC_MESSAGESmadLC_MESSAGESmagLC_MESSAGESmaiLC_MESSAGESmakLC_MESSAGESmanLC_MESSAGESmapLC_MESSAGESmasLC_MESSAGESmdfLC_MESSAGESmdrLC_MESSAGESmenLC_MESSAGESmgLC_MESSAGESmgaLC_MESSAGESmhLC_MESSAGESmiLC_MESSAGESmicLC_MESSAGESminLC_MESSAGESmisLC_MESSAGESmkLC_MESSAGESmkhLC_MESSAGESmlLC_MESSAGESmnLC_MESSAGESmncLC_MESSAGESmniLC_MESSAGESmnoLC_MESSAGESmoLC_MESSAGESmohLC_MESSAGESmosLC_MESSAGESmrLC_MESSAGESmsLC_MESSAGESms_MYLC_MESSAGESmtLC_MESSAGESmulLC_MESSAGESmunLC_MESSAGESmusLC_MESSAGESmwlLC_MESSAGESmwrLC_MESSAGESmyLC_MESSAGESmy_MMLC_MESSAGESmynLC_MESSAGESmyvLC_MESSAGESnaLC_MESSAGESnahLC_MESSAGESnaiLC_MESSAGESnapLC_MESSAGESnbLC_MESSAGESnb_NOLC_MESSAGESndLC_MESSAGESndsLC_MESSAGESnds@NFELC_MESSAGESnds_DELC_MESSAGESneLC_MESSAGESnewLC_MESSAGESngLC_MESSAGESniaLC_MESSAGESnicLC_MESSAGESniuLC_MESSAGESnlLC_MESSAGESnl_BELC_MESSAGESnl_NLLC_MESSAGESnnLC_MESSAGESnoLC_MESSAGESnogLC_MESSAGESnonLC_MESSAGESnqoLC_MESSAGESnrLC_MESSAGESnsoLC_MESSAGESnubLC_MESSAGESnvLC_MESSAGESnwcLC_MESSAGESnyLC_MESSAGESnymLC_MESSAGESnynLC_MESSAGESnyoLC_MESSAGESnziLC_MESSAGESocLC_MESSAGESojLC_MESSAGESomLC_MESSAGESorLC_MESSAGESosLC_MESSAGESosaLC_MESSAGESotaLC_MESSAGESotoLC_MESSAGESpaLC_MESSAGESpaaLC_MESSAGESpagLC_MESSAGESpalLC_MESSAGESpamLC_MESSAGESpapLC_MESSAGESpauLC_MESSAGESpeoLC_MESSAGESphiLC_MESSAGESphnLC_MESSAGESpiLC_MESSAGESplLC_MESSAGESpl_PLLC_MESSAGESponLC_MESSAGESpraLC_MESSAGESproLC_MESSAGESpsLC_MESSAGESptLC_MESSAGESpt_BRLC_MESSAGESpt_PTLC_MESSAGESqaa-qtzLC_MESSAGESquLC_MESSAGESrajLC_MESSAGESrapLC_MESSAGESrarLC_MESSAGESrmLC_MESSAGESrnLC_MESSAGESroLC_MESSAGESroaLC_MESSAGESromLC_MESSAGESruLC_MESSAGESru_RULC_MESSAGESrupLC_MESSAGESrwLC_MESSAGESsaLC_MESSAGESsadLC_MESSAGESsahLC_MESSAGESsaiLC_MESSAGESsalLC_MESSAGESsamLC_MESSAGESsasLC_MESSAGESsatLC_MESSAGESscLC_MESSAGESscnLC_MESSAGESscoLC_MESSAGESsdLC_MESSAGESseLC_MESSAGESselLC_MESSAGESsemLC_MESSAGESsgLC_MESSAGESsgaLC_MESSAGESsgnLC_MESSAGESshnLC_MESSAGESsiLC_MESSAGESsidLC_MESSAGESsioLC_MESSAGESsitLC_MESSAGESskLC_MESSAGESslLC_MESSAGESsl_SILC_MESSAGESslaLC_MESSAGESsmLC_MESSAGESsmaLC_MESSAGESsmiLC_MESSAGESsmjLC_MESSAGESsmnLC_MESSAGESsmsLC_MESSAGESsnLC_MESSAGESsnkLC_MESSAGESsoLC_MESSAGESsogLC_MESSAGESsonLC_MESSAGESsqLC_MESSAGESsq_ALLC_MESSAGESsrLC_MESSAGESsr@LatnLC_MESSAGESsr@ijeLC_MESSAGESsr@ijekavianLC_MESSAGESsr@ijekavianlatinLC_MESSAGESsr@latinLC_MESSAGESsr_RSLC_MESSAGESsrnLC_MESSAGESsrrLC_MESSAGESssLC_MESSAGESssaLC_MESSAGESstLC_MESSAGESsuLC_MESSAGESsukLC_MESSAGESsusLC_MESSAGESsuxLC_MESSAGESsvLC_MESSAGESsv_SELC_MESSAGESswLC_MESSAGESsycLC_MESSAGESsyrLC_MESSAGEStaLC_MESSAGEStaiLC_MESSAGESteLC_MESSAGEStemLC_MESSAGESterLC_MESSAGEStetLC_MESSAGEStgLC_MESSAGESthLC_MESSAGEStiLC_MESSAGEStigLC_MESSAGEStivLC_MESSAGEStkLC_MESSAGEStklLC_MESSAGEStlLC_MESSAGEStlhLC_MESSAGEStliLC_MESSAGEStmhLC_MESSAGEStnLC_MESSAGEStoLC_MESSAGEStogLC_MESSAGEStpiLC_MESSAGEStrLC_MESSAGEStsLC_MESSAGEStsiLC_MESSAGESttLC_MESSAGEStumLC_MESSAGEStupLC_MESSAGEStutLC_MESSAGEStvlLC_MESSAGEStwLC_MESSAGEStyLC_MESSAGEStyvLC_MESSAGESudmLC_MESSAGESugLC_MESSAGESugaLC_MESSAGESukLC_MESSAGESuk_UALC_MESSAGESumbLC_MESSAGESundLC_MESSAGESurLC_MESSAGESur_PKLC_MESSAGESuzLC_MESSAGESuz@LatnLC_MESSAGESuz@cyrillicLC_MESSAGESvaiLC_MESSAGESveLC_MESSAGESviLC_MESSAGESvoLC_MESSAGESvotLC_MESSAGESwaLC_MESSAGESwakLC_MESSAGESwalLC_MESSAGESwarLC_MESSAGESwasLC_MESSAGESwenLC_MESSAGESwoLC_MESSAGESxalLC_MESSAGESxhLC_MESSAGESyaoLC_MESSAGESyapLC_MESSAGESyiLC_MESSAGESyoLC_MESSAGESypkLC_MESSAGESzaLC_MESSAGESzapLC_MESSAGESzblLC_MESSAGESzenLC_MESSAGESzhLC_MESSAGESzh_CNzh_CN.GB2312LC_MESSAGESLC_MESSAGESzh_HKLC_MESSAGESzh_TWzh_TW.Big5LC_MESSAGESLC_MESSAGESzndLC_MESSAGESzuLC_MESSAGESzunLC_MESSAGESzxxLC_MESSAGESzzaLC_MESSAGESmanaaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannabman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannaceman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannachman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannadaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannadyman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannaeman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannafman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannaf_ZAman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannafaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannafhman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannainman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannakman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannakkman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannaleman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannalgman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannaltman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannamman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannam_ETman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannanman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannangman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannanpman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannapaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannarman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannarcman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannarnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannarpman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannartman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannarwman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannasman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannastman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannast_ESman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannathman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannausman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannavman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannawaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannayman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannazman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannaz_IRman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbadman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbaiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbalman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbanman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbasman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbatman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbeman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbe@latinman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbejman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbemman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannberman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbgman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbg_BGman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbhman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbhoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbikman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbinman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannblaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbmman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbn_INman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbntman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannboman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbraman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbsman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbtkman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbuaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbugman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannbynman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannca@valenciaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannca_ESman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannca_ES@valencianman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncadman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncaiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncarman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncauman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannceman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncebman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncelman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannchman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannchbman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannchgman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannchkman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannchmman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannchnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannchoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannchpman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannchrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannchyman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncmcman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncopman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncpeman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncpfman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncppman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncrhman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncrpman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncsman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncs_CZman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncsbman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncuman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncusman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncvman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanncyman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndakman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndarman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndayman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndeman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannde_ATman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannde_CHman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannde_DEman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndefaultman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndelman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndenman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndgrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndinman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndoiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndraman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndsbman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannduaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndumman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndvman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndyuman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanndzman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanneeman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannefiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannegyman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannekaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannelman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannel_GRman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannelxman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannenman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannen@boldquotman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannen@quotman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannen@shawman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannen_AUman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannen_CAman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannen_GBman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannen_NZman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannen_USman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannenmman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanneoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannesman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_ARman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_CLman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_COman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_CRman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_DOman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_ECman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_ESman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_GTman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_HNman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_MXman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_NIman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_PAman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_PEman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_PRman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_SVman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_UYman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannes_VEman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannetman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannet_EEman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanneuman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanneu_ESman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannewoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfa_IRman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfanman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfatman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannffman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfi_FIman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfilman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfiuman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfjman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfonman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfr_BEman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfr_CAman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfr_CHman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfr_FRman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfrmman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfroman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfrrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfrsman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfurman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannfyman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngaaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngayman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngbaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngdman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngemman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngezman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngilman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannglman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngl_ESman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngmhman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngohman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngonman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngorman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngotman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngrbman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngrcman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngswman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannguman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngvman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanngwiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhaiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhawman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannheman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhe_ILman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhilman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhimman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhitman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhmnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhr_HRman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhsbman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhtman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhuman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhu_HUman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhupman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhyman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannhzman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanniaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannibaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannidman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannieman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannigman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanniiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannijoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannikman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanniloman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannincman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannineman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanninhman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannioman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanniraman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanniroman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannisman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannitman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannit_CHman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannit_ITman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanniuman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannjaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannja_JPman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannjboman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannjprman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannjrbman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannjvman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkaaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkabman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkacman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkamman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkarman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkawman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkbdman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkgman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkhaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkhiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkhoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkjman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkkman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannklman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkmman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkmbman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannknman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannko_KRman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkokman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkosman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkpeman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkrcman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkrlman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkroman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkruman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannksman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannks@devanagariman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkuman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkumman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkutman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkvman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkwman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannkyman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannlaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannladman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannlahman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannlamman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannlbman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannlezman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannlgman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannliman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannlnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannloman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannlolman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannlozman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannltman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannluman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannluaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannluiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannlunman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannluoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannlusman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannlvman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannlv_LVman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmadman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmagman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmaiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmakman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmanman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmapman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmasman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmdfman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmdrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmenman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmgman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmgaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmhman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmicman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannminman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmisman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmkman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmkhman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmlman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmncman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmniman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmnoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmohman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmosman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmsman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannms_MYman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmtman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmulman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmunman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmusman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmwlman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmwrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmyman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmy_MMman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmynman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmyvman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnahman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnaiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnapman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnbman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnb_NOman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannndman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannndsman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnds@NFEman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnds_DEman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannneman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnewman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannngman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannniaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnicman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannniuman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnlman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnl_BEman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnl_NLman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnogman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnonman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnqoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnsoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnubman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnvman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnwcman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnyman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnymman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnynman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnyoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannnziman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannocman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannojman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannomman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannorman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannosman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannosaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannotaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannotoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannpaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannpaaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannpagman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannpalman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannpamman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannpapman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannpauman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannpeoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannphiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannphnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannpiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannplman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannpl_PLman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannponman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannpraman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannproman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannpsman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannptman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannpt_BRman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannpt_PTman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannqaa-qtzman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannquman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannrajman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannrapman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannrarman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannrmman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannrnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannroman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannroaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannromman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannruman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannru_RUman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannrupman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannrwman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsadman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsahman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsaiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsalman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsamman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsasman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsatman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannscman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannscnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannscoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsdman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannseman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannselman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsemman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsgman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsgaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsgnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannshnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsidman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsioman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsitman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannskman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannslman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsl_SIman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannslaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsmman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsmaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsmiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsmjman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsmnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsmsman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsnkman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsogman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsonman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsqman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsq_ALman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsr@Latnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsr@ijeman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsr@ijekavianman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsr@ijekavianlatinman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsr@latinman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsr_RSman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsrnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsrrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannssman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannssaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannstman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsuman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsukman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsusman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsuxman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsvman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsv_SEman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannswman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsycman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannsyrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntaiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannteman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntemman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannterman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntetman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntgman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannthman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntigman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntivman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntkman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntklman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntlman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntlhman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntliman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntmhman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntogman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntpiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntrman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntsman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntsiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannttman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntumman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntupman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntutman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntvlman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntwman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntyman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmanntyvman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannudmman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannugman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannugaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannukman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannuk_UAman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannumbman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannundman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannurman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannur_PKman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannuzman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannuz@Latnman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannuz@cyrillicman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannvaiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannveman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannviman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannvoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannvotman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannwaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannwakman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannwalman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannwarman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannwasman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannwenman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannwoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannxalman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannxhman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannyaoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannyapman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannyiman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannyoman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannypkman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannzaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannzapman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannzblman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannzenman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannzhman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannzh_CNzh_CN.GB2312man0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannzh_HKman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannzh_TWzh_TW.Big5man0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannzndman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannzuman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannzunman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannzxxman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannzzaman0pman1man1pman1xman2man2xman3man3pman3xman4man4xman5man5xman6man6xman7man7xman8man8xman9man9xmannmime-infomiscomfpixmapssoundsthemesxsessionssrcdebugkernelstmpvarcachedbemptygameslibgamesmisclocallocksubsyslogmailnisoptpreserverunspoollpdmailtmpyp//etc//etc/X11//etc/pm//etc/xdg//lib//run//usr//usr/lib//usr/lib/pm-utils//usr/local//usr/local/share//usr/local/share/man//usr/share//usr/share/augeas//usr/share/ghostscript//usr/share/locale//usr/share/locale/aa//usr/share/locale/ab//usr/share/locale/ace//usr/share/locale/ach//usr/share/locale/ada//usr/share/locale/ady//usr/share/locale/ae//usr/share/locale/af//usr/share/locale/af_ZA//usr/share/locale/afa//usr/share/locale/afh//usr/share/locale/ain//usr/share/locale/ak//usr/share/locale/akk//usr/share/locale/ale//usr/share/locale/alg//usr/share/locale/alt//usr/share/locale/am//usr/share/locale/am_ET//usr/share/locale/an//usr/share/locale/ang//usr/share/locale/anp//usr/share/locale/apa//usr/share/locale/ar//usr/share/locale/arc//usr/share/locale/arn//usr/share/locale/arp//usr/share/locale/art//usr/share/locale/arw//usr/share/locale/as//usr/share/locale/ast//usr/share/locale/ast_ES//usr/share/locale/ath//usr/share/locale/aus//usr/share/locale/av//usr/share/locale/awa//usr/share/locale/ay//usr/share/locale/az//usr/share/locale/az_IR//usr/share/locale/ba//usr/share/locale/bad//usr/share/locale/bai//usr/share/locale/bal//usr/share/locale/ban//usr/share/locale/bas//usr/share/locale/bat//usr/share/locale/be//usr/share/locale/be@latin//usr/share/locale/bej//usr/share/locale/bem//usr/share/locale/ber//usr/share/locale/bg//usr/share/locale/bg_BG//usr/share/locale/bh//usr/share/locale/bho//usr/share/locale/bi//usr/share/locale/bik//usr/share/locale/bin//usr/share/locale/bla//usr/share/locale/bm//usr/share/locale/bn//usr/share/locale/bn_IN//usr/share/locale/bnt//usr/share/locale/bo//usr/share/locale/br//usr/share/locale/bra//usr/share/locale/bs//usr/share/locale/btk//usr/share/locale/bua//usr/share/locale/bug//usr/share/locale/byn//usr/share/locale/ca//usr/share/locale/ca@valencia//usr/share/locale/ca_ES//usr/share/locale/ca_ES@valencian//usr/share/locale/cad//usr/share/locale/cai//usr/share/locale/car//usr/share/locale/cau//usr/share/locale/ce//usr/share/locale/ceb//usr/share/locale/cel//usr/share/locale/ch//usr/share/locale/chb//usr/share/locale/chg//usr/share/locale/chk//usr/share/locale/chm//usr/share/locale/chn//usr/share/locale/cho//usr/share/locale/chp//usr/share/locale/chr//usr/share/locale/chy//usr/share/locale/cmc//usr/share/locale/co//usr/share/locale/cop//usr/share/locale/cpe//usr/share/locale/cpf//usr/share/locale/cpp//usr/share/locale/cr//usr/share/locale/crh//usr/share/locale/crp//usr/share/locale/cs//usr/share/locale/cs_CZ//usr/share/locale/csb//usr/share/locale/cu//usr/share/locale/cus//usr/share/locale/cv//usr/share/locale/cy//usr/share/locale/da//usr/share/locale/dak//usr/share/locale/dar//usr/share/locale/day//usr/share/locale/de//usr/share/locale/de_AT//usr/share/locale/de_CH//usr/share/locale/de_DE//usr/share/locale/default//usr/share/locale/del//usr/share/locale/den//usr/share/locale/dgr//usr/share/locale/din//usr/share/locale/doi//usr/share/locale/dra//usr/share/locale/dsb//usr/share/locale/dua//usr/share/locale/dum//usr/share/locale/dv//usr/share/locale/dyu//usr/share/locale/dz//usr/share/locale/ee//usr/share/locale/efi//usr/share/locale/egy//usr/share/locale/eka//usr/share/locale/el//usr/share/locale/el_GR//usr/share/locale/elx//usr/share/locale/en//usr/share/locale/en@boldquot//usr/share/locale/en@quot//usr/share/locale/en@shaw//usr/share/locale/en_AU//usr/share/locale/en_CA//usr/share/locale/en_GB//usr/share/locale/en_NZ//usr/share/locale/en_US//usr/share/locale/enm//usr/share/locale/eo//usr/share/locale/es//usr/share/locale/es_AR//usr/share/locale/es_CL//usr/share/locale/es_CO//usr/share/locale/es_CR//usr/share/locale/es_DO//usr/share/locale/es_EC//usr/share/locale/es_ES//usr/share/locale/es_GT//usr/share/locale/es_HN//usr/share/locale/es_MX//usr/share/locale/es_NI//usr/share/locale/es_PA//usr/share/locale/es_PE//usr/share/locale/es_PR//usr/share/locale/es_SV//usr/share/locale/es_UY//usr/share/locale/es_VE//usr/share/locale/et//usr/share/locale/et_EE//usr/share/locale/eu//usr/share/locale/eu_ES//usr/share/locale/ewo//usr/share/locale/fa//usr/share/locale/fa_IR//usr/share/locale/fan//usr/share/locale/fat//usr/share/locale/ff//usr/share/locale/fi//usr/share/locale/fi_FI//usr/share/locale/fil//usr/share/locale/fiu//usr/share/locale/fj//usr/share/locale/fo//usr/share/locale/fon//usr/share/locale/fr//usr/share/locale/fr_BE//usr/share/locale/fr_CA//usr/share/locale/fr_CH//usr/share/locale/fr_FR//usr/share/locale/frm//usr/share/locale/fro//usr/share/locale/frr//usr/share/locale/frs//usr/share/locale/fur//usr/share/locale/fy//usr/share/locale/ga//usr/share/locale/gaa//usr/share/locale/gay//usr/share/locale/gba//usr/share/locale/gd//usr/share/locale/gem//usr/share/locale/gez//usr/share/locale/gil//usr/share/locale/gl//usr/share/locale/gl_ES//usr/share/locale/gmh//usr/share/locale/gn//usr/share/locale/goh//usr/share/locale/gon//usr/share/locale/gor//usr/share/locale/got//usr/share/locale/grb//usr/share/locale/grc//usr/share/locale/gsw//usr/share/locale/gu//usr/share/locale/gv//usr/share/locale/gwi//usr/share/locale/ha//usr/share/locale/hai//usr/share/locale/haw//usr/share/locale/he//usr/share/locale/he_IL//usr/share/locale/hi//usr/share/locale/hil//usr/share/locale/him//usr/share/locale/hit//usr/share/locale/hmn//usr/share/locale/ho//usr/share/locale/hr//usr/share/locale/hr_HR//usr/share/locale/hsb//usr/share/locale/ht//usr/share/locale/hu//usr/share/locale/hu_HU//usr/share/locale/hup//usr/share/locale/hy//usr/share/locale/hz//usr/share/locale/ia//usr/share/locale/iba//usr/share/locale/id//usr/share/locale/ie//usr/share/locale/ig//usr/share/locale/ii//usr/share/locale/ijo//usr/share/locale/ik//usr/share/locale/ilo//usr/share/locale/inc//usr/share/locale/ine//usr/share/locale/inh//usr/share/locale/io//usr/share/locale/ira//usr/share/locale/iro//usr/share/locale/is//usr/share/locale/it//usr/share/locale/it_CH//usr/share/locale/it_IT//usr/share/locale/iu//usr/share/locale/ja//usr/share/locale/ja_JP//usr/share/locale/jbo//usr/share/locale/jpr//usr/share/locale/jrb//usr/share/locale/jv//usr/share/locale/ka//usr/share/locale/kaa//usr/share/locale/kab//usr/share/locale/kac//usr/share/locale/kam//usr/share/locale/kar//usr/share/locale/kaw//usr/share/locale/kbd//usr/share/locale/kg//usr/share/locale/kha//usr/share/locale/khi//usr/share/locale/kho//usr/share/locale/ki//usr/share/locale/kj//usr/share/locale/kk//usr/share/locale/kl//usr/share/locale/km//usr/share/locale/kmb//usr/share/locale/kn//usr/share/locale/ko//usr/share/locale/ko_KR//usr/share/locale/kok//usr/share/locale/kos//usr/share/locale/kpe//usr/share/locale/kr//usr/share/locale/krc//usr/share/locale/krl//usr/share/locale/kro//usr/share/locale/kru//usr/share/locale/ks//usr/share/locale/ks@devanagari//usr/share/locale/ku//usr/share/locale/kum//usr/share/locale/kut//usr/share/locale/kv//usr/share/locale/kw//usr/share/locale/ky//usr/share/locale/la//usr/share/locale/lad//usr/share/locale/lah//usr/share/locale/lam//usr/share/locale/lb//usr/share/locale/lez//usr/share/locale/lg//usr/share/locale/li//usr/share/locale/ln//usr/share/locale/lo//usr/share/locale/lol//usr/share/locale/loz//usr/share/locale/lt//usr/share/locale/lu//usr/share/locale/lua//usr/share/locale/lui//usr/share/locale/lun//usr/share/locale/luo//usr/share/locale/lus//usr/share/locale/lv//usr/share/locale/lv_LV//usr/share/locale/mad//usr/share/locale/mag//usr/share/locale/mai//usr/share/locale/mak//usr/share/locale/man//usr/share/locale/map//usr/share/locale/mas//usr/share/locale/mdf//usr/share/locale/mdr//usr/share/locale/men//usr/share/locale/mg//usr/share/locale/mga//usr/share/locale/mh//usr/share/locale/mi//usr/share/locale/mic//usr/share/locale/min//usr/share/locale/mis//usr/share/locale/mk//usr/share/locale/mkh//usr/share/locale/ml//usr/share/locale/mn//usr/share/locale/mnc//usr/share/locale/mni//usr/share/locale/mno//usr/share/locale/mo//usr/share/locale/moh//usr/share/locale/mos//usr/share/locale/mr//usr/share/locale/ms//usr/share/locale/ms_MY//usr/share/locale/mt//usr/share/locale/mul//usr/share/locale/mun//usr/share/locale/mus//usr/share/locale/mwl//usr/share/locale/mwr//usr/share/locale/my//usr/share/locale/my_MM//usr/share/locale/myn//usr/share/locale/myv//usr/share/locale/na//usr/share/locale/nah//usr/share/locale/nai//usr/share/locale/nap//usr/share/locale/nb//usr/share/locale/nb_NO//usr/share/locale/nd//usr/share/locale/nds//usr/share/locale/nds@NFE//usr/share/locale/nds_DE//usr/share/locale/ne//usr/share/locale/new//usr/share/locale/ng//usr/share/locale/nia//usr/share/locale/nic//usr/share/locale/niu//usr/share/locale/nl//usr/share/locale/nl_BE//usr/share/locale/nl_NL//usr/share/locale/nn//usr/share/locale/no//usr/share/locale/nog//usr/share/locale/non//usr/share/locale/nqo//usr/share/locale/nr//usr/share/locale/nso//usr/share/locale/nub//usr/share/locale/nv//usr/share/locale/nwc//usr/share/locale/ny//usr/share/locale/nym//usr/share/locale/nyn//usr/share/locale/nyo//usr/share/locale/nzi//usr/share/locale/oc//usr/share/locale/oj//usr/share/locale/om//usr/share/locale/or//usr/share/locale/os//usr/share/locale/osa//usr/share/locale/ota//usr/share/locale/oto//usr/share/locale/pa//usr/share/locale/paa//usr/share/locale/pag//usr/share/locale/pal//usr/share/locale/pam//usr/share/locale/pap//usr/share/locale/pau//usr/share/locale/peo//usr/share/locale/phi//usr/share/locale/phn//usr/share/locale/pi//usr/share/locale/pl//usr/share/locale/pl_PL//usr/share/locale/pon//usr/share/locale/pra//usr/share/locale/pro//usr/share/locale/ps//usr/share/locale/pt//usr/share/locale/pt_BR//usr/share/locale/pt_PT//usr/share/locale/qaa-qtz//usr/share/locale/qu//usr/share/locale/raj//usr/share/locale/rap//usr/share/locale/rar//usr/share/locale/rm//usr/share/locale/rn//usr/share/locale/ro//usr/share/locale/roa//usr/share/locale/rom//usr/share/locale/ru//usr/share/locale/ru_RU//usr/share/locale/rup//usr/share/locale/rw//usr/share/locale/sa//usr/share/locale/sad//usr/share/locale/sah//usr/share/locale/sai//usr/share/locale/sal//usr/share/locale/sam//usr/share/locale/sas//usr/share/locale/sat//usr/share/locale/sc//usr/share/locale/scn//usr/share/locale/sco//usr/share/locale/sd//usr/share/locale/se//usr/share/locale/sel//usr/share/locale/sem//usr/share/locale/sg//usr/share/locale/sga//usr/share/locale/sgn//usr/share/locale/shn//usr/share/locale/si//usr/share/locale/sid//usr/share/locale/sio//usr/share/locale/sit//usr/share/locale/sk//usr/share/locale/sl//usr/share/locale/sl_SI//usr/share/locale/sla//usr/share/locale/sm//usr/share/locale/sma//usr/share/locale/smi//usr/share/locale/smj//usr/share/locale/smn//usr/share/locale/sms//usr/share/locale/sn//usr/share/locale/snk//usr/share/locale/so//usr/share/locale/sog//usr/share/locale/son//usr/share/locale/sq//usr/share/locale/sq_AL//usr/share/locale/sr//usr/share/locale/sr@Latn//usr/share/locale/sr@ije//usr/share/locale/sr@ijekavian//usr/share/locale/sr@ijekavianlatin//usr/share/locale/sr@latin//usr/share/locale/sr_RS//usr/share/locale/srn//usr/share/locale/srr//usr/share/locale/ss//usr/share/locale/ssa//usr/share/locale/st//usr/share/locale/su//usr/share/locale/suk//usr/share/locale/sus//usr/share/locale/sux//usr/share/locale/sv//usr/share/locale/sv_SE//usr/share/locale/sw//usr/share/locale/syc//usr/share/locale/syr//usr/share/locale/ta//usr/share/locale/tai//usr/share/locale/te//usr/share/locale/tem//usr/share/locale/ter//usr/share/locale/tet//usr/share/locale/tg//usr/share/locale/th//usr/share/locale/ti//usr/share/locale/tig//usr/share/locale/tiv//usr/share/locale/tk//usr/share/locale/tkl//usr/share/locale/tl//usr/share/locale/tlh//usr/share/locale/tli//usr/share/locale/tmh//usr/share/locale/tn//usr/share/locale/to//usr/share/locale/tog//usr/share/locale/tpi//usr/share/locale/tr//usr/share/locale/ts//usr/share/locale/tsi//usr/share/locale/tt//usr/share/locale/tum//usr/share/locale/tup//usr/share/locale/tut//usr/share/locale/tvl//usr/share/locale/tw//usr/share/locale/ty//usr/share/locale/tyv//usr/share/locale/udm//usr/share/locale/ug//usr/share/locale/uga//usr/share/locale/uk//usr/share/locale/uk_UA//usr/share/locale/umb//usr/share/locale/und//usr/share/locale/ur//usr/share/locale/ur_PK//usr/share/locale/uz//usr/share/locale/uz@Latn//usr/share/locale/uz@cyrillic//usr/share/locale/vai//usr/share/locale/ve//usr/share/locale/vi//usr/share/locale/vo//usr/share/locale/vot//usr/share/locale/wa//usr/share/locale/wak//usr/share/locale/wal//usr/share/locale/war//usr/share/locale/was//usr/share/locale/wen//usr/share/locale/wo//usr/share/locale/xal//usr/share/locale/xh//usr/share/locale/yao//usr/share/locale/yap//usr/share/locale/yi//usr/share/locale/yo//usr/share/locale/ypk//usr/share/locale/za//usr/share/locale/zap//usr/share/locale/zbl//usr/share/locale/zen//usr/share/locale/zh//usr/share/locale/zh_CN.GB2312//usr/share/locale/zh_CN//usr/share/locale/zh_HK//usr/share/locale/zh_TW.Big5//usr/share/locale/zh_TW//usr/share/locale/znd//usr/share/locale/zu//usr/share/locale/zun//usr/share/locale/zxx//usr/share/locale/zza//usr/share/man//usr/share/man/aa//usr/share/man/ab//usr/share/man/ace//usr/share/man/ach//usr/share/man/ada//usr/share/man/ady//usr/share/man/ae//usr/share/man/af//usr/share/man/af_ZA//usr/share/man/afa//usr/share/man/afh//usr/share/man/ain//usr/share/man/ak//usr/share/man/akk//usr/share/man/ale//usr/share/man/alg//usr/share/man/alt//usr/share/man/am//usr/share/man/am_ET//usr/share/man/an//usr/share/man/ang//usr/share/man/anp//usr/share/man/apa//usr/share/man/ar//usr/share/man/arc//usr/share/man/arn//usr/share/man/arp//usr/share/man/art//usr/share/man/arw//usr/share/man/as//usr/share/man/ast//usr/share/man/ast_ES//usr/share/man/ath//usr/share/man/aus//usr/share/man/av//usr/share/man/awa//usr/share/man/ay//usr/share/man/az//usr/share/man/az_IR//usr/share/man/ba//usr/share/man/bad//usr/share/man/bai//usr/share/man/bal//usr/share/man/ban//usr/share/man/bas//usr/share/man/bat//usr/share/man/be//usr/share/man/be@latin//usr/share/man/bej//usr/share/man/bem//usr/share/man/ber//usr/share/man/bg//usr/share/man/bg_BG//usr/share/man/bh//usr/share/man/bho//usr/share/man/bi//usr/share/man/bik//usr/share/man/bin//usr/share/man/bla//usr/share/man/bm//usr/share/man/bn//usr/share/man/bn_IN//usr/share/man/bnt//usr/share/man/bo//usr/share/man/br//usr/share/man/bra//usr/share/man/bs//usr/share/man/btk//usr/share/man/bua//usr/share/man/bug//usr/share/man/byn//usr/share/man/ca//usr/share/man/ca@valencia//usr/share/man/ca_ES//usr/share/man/ca_ES@valencian//usr/share/man/cad//usr/share/man/cai//usr/share/man/car//usr/share/man/cau//usr/share/man/ce//usr/share/man/ceb//usr/share/man/cel//usr/share/man/ch//usr/share/man/chb//usr/share/man/chg//usr/share/man/chk//usr/share/man/chm//usr/share/man/chn//usr/share/man/cho//usr/share/man/chp//usr/share/man/chr//usr/share/man/chy//usr/share/man/cmc//usr/share/man/co//usr/share/man/cop//usr/share/man/cpe//usr/share/man/cpf//usr/share/man/cpp//usr/share/man/cr//usr/share/man/crh//usr/share/man/crp//usr/share/man/cs//usr/share/man/cs_CZ//usr/share/man/csb//usr/share/man/cu//usr/share/man/cus//usr/share/man/cv//usr/share/man/cy//usr/share/man/da//usr/share/man/dak//usr/share/man/dar//usr/share/man/day//usr/share/man/de//usr/share/man/de_AT//usr/share/man/de_CH//usr/share/man/de_DE//usr/share/man/default//usr/share/man/del//usr/share/man/den//usr/share/man/dgr//usr/share/man/din//usr/share/man/doi//usr/share/man/dra//usr/share/man/dsb//usr/share/man/dua//usr/share/man/dum//usr/share/man/dv//usr/share/man/dyu//usr/share/man/dz//usr/share/man/ee//usr/share/man/efi//usr/share/man/egy//usr/share/man/eka//usr/share/man/el//usr/share/man/el_GR//usr/share/man/elx//usr/share/man/en//usr/share/man/en@boldquot//usr/share/man/en@quot//usr/share/man/en@shaw//usr/share/man/en_AU//usr/share/man/en_CA//usr/share/man/en_GB//usr/share/man/en_NZ//usr/share/man/en_US//usr/share/man/enm//usr/share/man/eo//usr/share/man/es//usr/share/man/es_AR//usr/share/man/es_CL//usr/share/man/es_CO//usr/share/man/es_CR//usr/share/man/es_DO//usr/share/man/es_EC//usr/share/man/es_ES//usr/share/man/es_GT//usr/share/man/es_HN//usr/share/man/es_MX//usr/share/man/es_NI//usr/share/man/es_PA//usr/share/man/es_PE//usr/share/man/es_PR//usr/share/man/es_SV//usr/share/man/es_UY//usr/share/man/es_VE//usr/share/man/et//usr/share/man/et_EE//usr/share/man/eu//usr/share/man/eu_ES//usr/share/man/ewo//usr/share/man/fa//usr/share/man/fa_IR//usr/share/man/fan//usr/share/man/fat//usr/share/man/ff//usr/share/man/fi//usr/share/man/fi_FI//usr/share/man/fil//usr/share/man/fiu//usr/share/man/fj//usr/share/man/fo//usr/share/man/fon//usr/share/man/fr//usr/share/man/fr_BE//usr/share/man/fr_CA//usr/share/man/fr_CH//usr/share/man/fr_FR//usr/share/man/frm//usr/share/man/fro//usr/share/man/frr//usr/share/man/frs//usr/share/man/fur//usr/share/man/fy//usr/share/man/ga//usr/share/man/gaa//usr/share/man/gay//usr/share/man/gba//usr/share/man/gd//usr/share/man/gem//usr/share/man/gez//usr/share/man/gil//usr/share/man/gl//usr/share/man/gl_ES//usr/share/man/gmh//usr/share/man/gn//usr/share/man/goh//usr/share/man/gon//usr/share/man/gor//usr/share/man/got//usr/share/man/grb//usr/share/man/grc//usr/share/man/gsw//usr/share/man/gu//usr/share/man/gv//usr/share/man/gwi//usr/share/man/ha//usr/share/man/hai//usr/share/man/haw//usr/share/man/he//usr/share/man/he_IL//usr/share/man/hi//usr/share/man/hil//usr/share/man/him//usr/share/man/hit//usr/share/man/hmn//usr/share/man/ho//usr/share/man/hr//usr/share/man/hr_HR//usr/share/man/hsb//usr/share/man/ht//usr/share/man/hu//usr/share/man/hu_HU//usr/share/man/hup//usr/share/man/hy//usr/share/man/hz//usr/share/man/ia//usr/share/man/iba//usr/share/man/id//usr/share/man/ie//usr/share/man/ig//usr/share/man/ii//usr/share/man/ijo//usr/share/man/ik//usr/share/man/ilo//usr/share/man/inc//usr/share/man/ine//usr/share/man/inh//usr/share/man/io//usr/share/man/ira//usr/share/man/iro//usr/share/man/is//usr/share/man/it//usr/share/man/it_CH//usr/share/man/it_IT//usr/share/man/iu//usr/share/man/ja//usr/share/man/ja_JP//usr/share/man/jbo//usr/share/man/jpr//usr/share/man/jrb//usr/share/man/jv//usr/share/man/ka//usr/share/man/kaa//usr/share/man/kab//usr/share/man/kac//usr/share/man/kam//usr/share/man/kar//usr/share/man/kaw//usr/share/man/kbd//usr/share/man/kg//usr/share/man/kha//usr/share/man/khi//usr/share/man/kho//usr/share/man/ki//usr/share/man/kj//usr/share/man/kk//usr/share/man/kl//usr/share/man/km//usr/share/man/kmb//usr/share/man/kn//usr/share/man/ko//usr/share/man/ko_KR//usr/share/man/kok//usr/share/man/kos//usr/share/man/kpe//usr/share/man/kr//usr/share/man/krc//usr/share/man/krl//usr/share/man/kro//usr/share/man/kru//usr/share/man/ks//usr/share/man/ks@devanagari//usr/share/man/ku//usr/share/man/kum//usr/share/man/kut//usr/share/man/kv//usr/share/man/kw//usr/share/man/ky//usr/share/man/la//usr/share/man/lad//usr/share/man/lah//usr/share/man/lam//usr/share/man/lb//usr/share/man/lez//usr/share/man/lg//usr/share/man/li//usr/share/man/ln//usr/share/man/lo//usr/share/man/lol//usr/share/man/loz//usr/share/man/lt//usr/share/man/lu//usr/share/man/lua//usr/share/man/lui//usr/share/man/lun//usr/share/man/luo//usr/share/man/lus//usr/share/man/lv//usr/share/man/lv_LV//usr/share/man/mad//usr/share/man/mag//usr/share/man/mai//usr/share/man/mak//usr/share/man/man//usr/share/man/map//usr/share/man/mas//usr/share/man/mdf//usr/share/man/mdr//usr/share/man/men//usr/share/man/mg//usr/share/man/mga//usr/share/man/mh//usr/share/man/mi//usr/share/man/mic//usr/share/man/min//usr/share/man/mis//usr/share/man/mk//usr/share/man/mkh//usr/share/man/ml//usr/share/man/mn//usr/share/man/mnc//usr/share/man/mni//usr/share/man/mno//usr/share/man/mo//usr/share/man/moh//usr/share/man/mos//usr/share/man/mr//usr/share/man/ms//usr/share/man/ms_MY//usr/share/man/mt//usr/share/man/mul//usr/share/man/mun//usr/share/man/mus//usr/share/man/mwl//usr/share/man/mwr//usr/share/man/my//usr/share/man/my_MM//usr/share/man/myn//usr/share/man/myv//usr/share/man/na//usr/share/man/nah//usr/share/man/nai//usr/share/man/nap//usr/share/man/nb//usr/share/man/nb_NO//usr/share/man/nd//usr/share/man/nds//usr/share/man/nds@NFE//usr/share/man/nds_DE//usr/share/man/ne//usr/share/man/new//usr/share/man/ng//usr/share/man/nia//usr/share/man/nic//usr/share/man/niu//usr/share/man/nl//usr/share/man/nl_BE//usr/share/man/nl_NL//usr/share/man/nn//usr/share/man/no//usr/share/man/nog//usr/share/man/non//usr/share/man/nqo//usr/share/man/nr//usr/share/man/nso//usr/share/man/nub//usr/share/man/nv//usr/share/man/nwc//usr/share/man/ny//usr/share/man/nym//usr/share/man/nyn//usr/share/man/nyo//usr/share/man/nzi//usr/share/man/oc//usr/share/man/oj//usr/share/man/om//usr/share/man/or//usr/share/man/os//usr/share/man/osa//usr/share/man/ota//usr/share/man/oto//usr/share/man/pa//usr/share/man/paa//usr/share/man/pag//usr/share/man/pal//usr/share/man/pam//usr/share/man/pap//usr/share/man/pau//usr/share/man/peo//usr/share/man/phi//usr/share/man/phn//usr/share/man/pi//usr/share/man/pl//usr/share/man/pl_PL//usr/share/man/pon//usr/share/man/pra//usr/share/man/pro//usr/share/man/ps//usr/share/man/pt//usr/share/man/pt_BR//usr/share/man/pt_PT//usr/share/man/qaa-qtz//usr/share/man/qu//usr/share/man/raj//usr/share/man/rap//usr/share/man/rar//usr/share/man/rm//usr/share/man/rn//usr/share/man/ro//usr/share/man/roa//usr/share/man/rom//usr/share/man/ru//usr/share/man/ru_RU//usr/share/man/rup//usr/share/man/rw//usr/share/man/sa//usr/share/man/sad//usr/share/man/sah//usr/share/man/sai//usr/share/man/sal//usr/share/man/sam//usr/share/man/sas//usr/share/man/sat//usr/share/man/sc//usr/share/man/scn//usr/share/man/sco//usr/share/man/sd//usr/share/man/se//usr/share/man/sel//usr/share/man/sem//usr/share/man/sg//usr/share/man/sga//usr/share/man/sgn//usr/share/man/shn//usr/share/man/si//usr/share/man/sid//usr/share/man/sio//usr/share/man/sit//usr/share/man/sk//usr/share/man/sl//usr/share/man/sl_SI//usr/share/man/sla//usr/share/man/sm//usr/share/man/sma//usr/share/man/smi//usr/share/man/smj//usr/share/man/smn//usr/share/man/sms//usr/share/man/sn//usr/share/man/snk//usr/share/man/so//usr/share/man/sog//usr/share/man/son//usr/share/man/sq//usr/share/man/sq_AL//usr/share/man/sr//usr/share/man/sr@Latn//usr/share/man/sr@ije//usr/share/man/sr@ijekavian//usr/share/man/sr@ijekavianlatin//usr/share/man/sr@latin//usr/share/man/sr_RS//usr/share/man/srn//usr/share/man/srr//usr/share/man/ss//usr/share/man/ssa//usr/share/man/st//usr/share/man/su//usr/share/man/suk//usr/share/man/sus//usr/share/man/sux//usr/share/man/sv//usr/share/man/sv_SE//usr/share/man/sw//usr/share/man/syc//usr/share/man/syr//usr/share/man/ta//usr/share/man/tai//usr/share/man/te//usr/share/man/tem//usr/share/man/ter//usr/share/man/tet//usr/share/man/tg//usr/share/man/th//usr/share/man/ti//usr/share/man/tig//usr/share/man/tiv//usr/share/man/tk//usr/share/man/tkl//usr/share/man/tl//usr/share/man/tlh//usr/share/man/tli//usr/share/man/tmh//usr/share/man/tn//usr/share/man/to//usr/share/man/tog//usr/share/man/tpi//usr/share/man/tr//usr/share/man/ts//usr/share/man/tsi//usr/share/man/tt//usr/share/man/tum//usr/share/man/tup//usr/share/man/tut//usr/share/man/tvl//usr/share/man/tw//usr/share/man/ty//usr/share/man/tyv//usr/share/man/udm//usr/share/man/ug//usr/share/man/uga//usr/share/man/uk//usr/share/man/uk_UA//usr/share/man/umb//usr/share/man/und//usr/share/man/ur//usr/share/man/ur_PK//usr/share/man/uz//usr/share/man/uz@Latn//usr/share/man/uz@cyrillic//usr/share/man/vai//usr/share/man/ve//usr/share/man/vi//usr/share/man/vo//usr/share/man/vot//usr/share/man/wa//usr/share/man/wak//usr/share/man/wal//usr/share/man/war//usr/share/man/was//usr/share/man/wen//usr/share/man/wo//usr/share/man/xal//usr/share/man/xh//usr/share/man/yao//usr/share/man/yap//usr/share/man/yi//usr/share/man/yo//usr/share/man/ypk//usr/share/man/za//usr/share/man/zap//usr/share/man/zbl//usr/share/man/zen//usr/share/man/zh//usr/share/man/zh_CN.GB2312//usr/share/man/zh_CN//usr/share/man/zh_HK//usr/share/man/zh_TW.Big5//usr/share/man/zh_TW//usr/share/man/znd//usr/share/man/zu//usr/share/man/zun//usr/share/man/zxx//usr/share/man/zza//usr/src//var//var/lib//var/lock//var/spool/-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tablescpioxz2i686-redhat-linux-gnudirectory?`7zXZ !#,C] b2~Rʢ 2y!3-3]ڨ=o)84굈A*M}6:|{ZV)EQHz>mb4KlAVt؂poF|X-qRrjCc5yXuWc;P%9᫏bY5^LIW:0v1OwYw/{ IXLRa^(kJ5{)A!Lr! QSwJ+_Z6kh{mE.~_+PUh̚Y5B\jWEg~~ܹ=ڦpL{'4@@*H>mƁ+o=0Pzt]?[C l=s/K>0^da (H3ĉqX(2VX3.vX7*FF;=!4Nrj+r x ձUU'o ]QсR@e6ӯiA-q 5,,kq3%mX4^+F;&U f2utD©ƃEl>p>N 7@XuތMz`#,ip|ίTaQ\{@Yjedž"DEr/tCF\|Z]nΥ~.x7zTz"zrX *F@B@QtK*FT00]RhbBl 0z.MԋުM?[Ff \m)߱REψ}U\'r6O^%_{^BLN7 wo@0!|H3eC?QŜrDŧ [E{S>~XT8DTMTis1m w=kAI+eB%=j&s9<\Dv.E4=v9${< ߺy9ʜ5#@Xn hLmf, D&-ߚ,2Nh~֐iV7~hŜ/[`]S#2z$"~0PfsV_PȞ-0aRZF t+w:@E=dW݃{xD:R8bP)` ^yl0CnP /@0YxEw. [|얱>+X4k̊&24 ۰OrbN!XH>d Y<53}uZ m_ |6zy+H^\O G8 /,kOOz07̝]ņlU\.oQĐ6Y6#k7f*㻔b05ܻqgy3!#?0[j,-W1g5 Ģ="@q zuJ qZv!MEAkX7S9IhL=y6>k"|)ck΂3)÷yip ѪC~g cn>S*yfwo9jSg+hl̢gV_7ˎG\V HGf}ޜ []P.>ӖX扙6x >++BX@uwJBjF:ot>M?]!">hԛ:nFkgqPC_ 8tJ8Zg?W /Qh7Cu0[ʢdצ ]M9@Ň^k% ~ō72o 2V' B_es75SAl)'gm"bxZj1G#mĨ<Ŏz e=ݙ/| _ۖ幭w~,N`#w)C<0h7&p<;ƠK"5u-a# S.1gx&|=0|*5p"UVS\en̫sy8,@m_2ߚST߃20a`oHiEuql^d(v&\pCw!M |Tx$NXjQX YTaW:o W!kof }0sh- IvT߿wXjR~xxq.M>9W"Шxj>#NŊ=~*x'[.N،Ow-OBt)j2Xs`0:Li~wN}|2sŨF׋>EJ-m)Qc4n{0gEl-!\-8dK# 7t#at¡<uy] (7\6ٕ QOk|IP%36\kYkdE6ޛx:;VSL_E9()!Bh{16c,R bS w)"%Ϙ+yOmI3A Gx&&2 2 Ѷ8g@C_{c<,^Nr.8TrPHM`H>emJ& yv9b *vPew?J+bSnj0G=xTl;K#L2:6ĕ|py7F?ii0?|-l5zV ;pE.AMt܁5ɗL)ffp+r+5Z/p6O.˪ niH8X oDjF"<w~ҏFQ .$W26|qɂR[&%;kz cCmۤӡEUZ @sj,Ck_Pvx_*>B?DbQҍ,JWkdzΖT HKao]7 greBJ$K @v`M*l P =aATxK{f?)iǡXHkMāGmtW 6rF&ie&k]^όBY⎭** lTIG Ί48Ks#ZD %[kd1u^pr.֎$φ; duwۘۖ#kZbQ.vow;*6N&ckYWMd|ba-y\u**זZ6 wD:!CIθ.48nv객Q2hnb%׊I/A*'i0DɒV!jMr/*SkS Z46QpW0vZu0 7 '8"=p b.ج;bs .+E P^rvA{5?% `țnm)L?;+P{IXt0Ӭ?=JfR&FrK_vc sUB!M{ndwZ0Q!!^'O{"R{CB!-78 >G5-p ((/7x##?5V)^&z2l¨{hEJ9j`0ȿgFaƄv߷@`< NDDeiHu9teF= wJ)?Ajڲtt (w%ŵaaJ\%,IX/ۻؖ:1x#[~ #vpDY@X ~$kymr3(gmlb;# աH15\ԨA\ȴi/k̴*W;%85G;tj b%"nɑq"|XJ֙J zC4N۰h*׋4.(<&B>׫w2DLEQ ~1c!pV<~HuSTŝ~X,RGM~$(r4he @5mE9oUTC]ѭef&ci6]ՙ,r50rx-sH;xl=/gVqkkt"fG[ 7*՝Q5+哂!$S?sxMb]-(w+ A7<컕s;XA'sBICXmPQߛТh]/|"x]GnAlr| _ GRQq=*_gϋds4tdqyŀ1{);N!k}O3w|sIqB %5h袑 +%ז$r v8c)W؂A$d h7WꈖG,6Wk.X hA-wy'/t()hE`^_.@wK <`t~)oźvHFY8'8 />?Cs}89mjYmNwrރMc:3p#ϊj̃9FYnq!R 8%낓0: w\q]x2V)@g2>ZI 8Oxߛ xw}0S@+7/vnRqGmF8W}oyg>DVT| H*DIPV $ӹH(̀;qٱ@ɮv.gq!uPNcnD\YB AJaMhiM`i2fbii&##ISjidhL i'h'z1TjЀ&&OSh4Mɱ & 0'j 24MC@4 4 h4"""hi5<̚Sѐdi < I=LOM2e=5='6LM&3L43IyOSCL4b0b56'OԤLSDS2a= h  Ph44j@Bm=&ɚIlF2m&2lhсѤО3)Ɣ43SL4fidTJ@h `jmS i=@P!MC@h@{A:=ˣE!sHݮ;ÇW\A7F P*m:3Q ёj$Q(4/0hܩRK& '=F-WWAdD4-zM+eV[_VKĕ:Hb{&`4 [5P٭8j\{[+h1&cPY[T:ǟ3nnfj#:S(!2R$."?#v|}$>rdY>Z. ڕ qّ4\yz6:oCXpHXN4gݗ`lUTZF䆡QQiYg?歊]y}'goO*g IHImp|B=k|t_xs0#kzއ=Czk ؏y H3iHUEG 4Mo -|?a`uR\|J">nyN!ugq<:__KcE9\ebةL.p2y-|z}98C8L +:PXW, ‘8 ҏl.jC QY|b`ݑe!>uQ +lG֑- PW^I06ok6 !{ $I@Il&I/&gi"IcM-[qu2Zek:VNoW7;>^ 0yyz='O|qYƦ!~pEn~M p3>CF2GLQL3\iXUV|)ᬬe 1a8ZuAb帶/DݿMEU$S9?DNTngWoK|_U͟kj#?ɳڭs?U{aOe{o߬WksTz=2DKR;&&io?3%%}_o2N6's3xohgi k]..];4Us:!* 仺'aO]5 `,rb9MKߧ^AeTGf[c4k&auc8L9.χ]~N\_Ekc3=koWnv|ZIr%Lmm΀ggc&gنM+8[!E{#4Z(6_;8oeof-5̵;-UNַzDwXH!% eJ(00I'4RC7Ǐ{-ksUW[቙lfg~ZzIom8UVIt mLΏtc2<}LK3fg&gmdm|ENO'ֶwUW!m?cbfg?T32Wjxȏ ~6jfo#UP; ^f 5yU"?lDMٞgu{a~qt?zֶ9USo3)-]L L|m%ɶoi8LH%뻾#owWjwUqu^^J:1<<0W]X:qEWc{[[oU[}[jKK-'*CsX*TN۶>s6Ka1i|#Y3*L9+md=gyW8EeY-l/y;aG3#> o}L=~׿#,omUo3<<MoKK}I~moUl-oe7?4o['ݛpoi7ߋfϯpsy` oQ{WuvSL^dLi<=?{]$X5ۯIB{8ۯ}xAi>387KK@~gKlmI@ k?9ʯ٥@>fg*\Wg幩03Y]̉巆D]3k;ԙȗkoMqTNK` @]aGqo8uSn;&3{K"-Z#?Uk}lKf4 -IUiCXT6(6s]BGȠS#T9d":]MTzn]ݹo1vcvJ3L|O{zoe%@ӉoKם3t?R%K7 Ibd%6_R߷x{v|\-k%0e~-D=F{QA,SL;,1(AE` _u?6q6:Lhx^-{t!cƂ<!M.Gt<i6C$$#V\=d#vi4ɛ[e %c [$%[pbX2DcϬH""Fe#B%\kv6af%6&DBԁoo1KeCfm<&+LD˲9M #VsjI`ݩm,-KUQnYW,f*G Ĉ$Ddj%\eb1dnYw]cFa 15j@;v#b1F 2FHvZS.e7gKm_KC`7{3ѥ_oj,Km~OVX=)/; g3? L?{>SrHUl0Ȫf|6RRцhz:I1s^wZxWU0귻L73;g=Z'5^DV fbߓm/jx̥R{lba-Z^ 0ݻ.2gɖg~ 4Uj=|nxZCGK\pAm|sW AQC5pACkfq 4[gL#owLRܤO` y&{S$}I-mQ7a3ǶA.%lȁn&^Y3{FQ"v7^(9y){ xΫ[ U,eV\ͦ 4Zf kp˛{_ 釐I)"~ Ϊ^Sdбٽ&D"di qyB޽/%-Է%^ m%=26I> Ϙ4~O'Kn[G$}VX7m[hS~f /ə٨7뼋XE!y#s6|C{ʯ/K7!1./o=r_Y-@K"\ni7D ۨl6[5L[XڹUb-ǮmDվ9"Ifx, r *iK*TV#~rSE7KAcŽbn1mdP񪶵9*tZ!@@ٍT$P&κI A%X&|WݒxȈ"3%0 -nހ1#;ޭm0ݠDa<'Έ M5?vp2<=^"">%Z^G 33"-UhC2O-#I,Ka;24Ay%:d8$:[\g%T`Nfb@S8X^D Z <&uw|;Cotl߈ wi6$'\ff O$NC{Iu X6N.&edoy: :?5!w݄.n %I=(Hflz ~㹙)wr R j]%7.žjm3D o@"I͈$?JHJLΑ ir{=߶i?v'ڮn Wq6$w'8ᅨdu[D]_-gq&|7c32K %}g$O$O $I=xߞiliΈkOKz ډߙ4&CXm* }x[}dN M%ۥo|ߡ1g7& x=&ƺc췯&e3۶OKO? XNwSg_|;]W3=[W_.>im'%9րDv6wv$I*q, xqO,\N#Ip|>O uN6?\x<1_|&.); ..)G?^V_$9y~~_Ob+NiTk%)JR)J]JR.)JR.)JR R)JR)JR)JR)JR)JR)J^ƫaaaa/aaa3<B!B!B!B)R7ڢ!B!B!BR 8fLB!B!B!BT B!B!B!B)Rs7TB!B!B)R7!B,T$ښڪNa_`=cbc@5\&XNZ7JTM-B.V?mV׶mkm5ih/&FVkvLkʕJ}!m36c>^ 99zT١se%=fr^־mƬ7v% m/!/]*#XՏ4j5̬=R:$%fB}^>Uw2X}sBVYa^xn:7SCKͥ޷|s[fdfG#{)Z}nmtimGVT~?MI ~3e8bdѸGj33ϯR,2Jl7oZ4&$bt4o R2YH#װHRR75*DI'q<+1X;Ú5VvY)ǷDT'.3 E]kQ([--C75Q6꽩hPVvymFl^}q+Xͩ ';@m>Z45%;t>iQ|e&r *aV9JrIQEKlkeyBݠZJ4BjY+hD '`$-EvJns'M'IxϮ2-[=maLs͖y{;s{0/$ap@VUZ[lm\\\ZNman1j4rvK*s]R@;]&aV*OFCj4gV.,[pϹ+cE7ɡ`>>yMsʨ4m"hRXQ65jC¬z^鰐ɻ0FfLq–h-kղk'(O6z+q/Q ]ˬ||M֎Ţ%v=#rdvPΧ,-jc.uʱ*-z i'h_ga3ҏ紕N)`νSܯEU*%J(ʺX& z{yʸ$nfDgߧnRҪt om DuL$la< vLr4kJSV(bYmER%]]G"qɊZjcm4٧Ok`PhbO!A= "B_NZBV6՗Im 1&5(gm3434whj)j**\"X&*dXU&rX:"VѤJ~­:JەH9},[MdJ)g]["}Dx[Rvg @xкb `X"2nR۫dK,1`& 0a^akmcK.o4(Wg7d$ b HPB! $$h^g_N~S@ Teej*:?VuޅoK$d#~0>_[OBr=zp [)Kl]UI+=̫m-l46m@ S_;ᄉ()VV}l~ 9""vluHNC.EayG9:Jy,j V:YemKUf2D4gu[~8P@O+c EiDf@")h ̈́e8[[6[m+J& tj G֖Eۆ3/ @[mm[gŤ[e+VN "~c{y+%4Kc[[Sb$-7./g g6n{ռ NJKLzuWv_W~6C՛ig1+,!}ns?vL>k3ht,({=N.3u?R's^f4?a"vӷ\SGuAyܲ{%d7\nlmBWи7?q7Qߕt+?es᦬@>UB~{= u[oևe>6,Sd֛=BK\lb_}ou2;N|^<]yE~>+=Fqb0bN"ձn[10& w}7H,לߏcd6G|E 7jͷƄ[p|N񹈓Erx>֦w/tQ%ÿ8쥡 i[Gjz3.J ox\ |S:ֺn$y:lj ɼ%qƹ F-M.-)} #J泆i{7lweqd/eeN~GZˤč0r{v/7xihpLԹX jc~ŏelϧ}^o73UK/np<%>5&>}ۡSЭCd{/7\{{~d߯ܟX}gria9ebL+?j8-\_ڨ1o6-T1qG=|gR]0i'ŷ[{8b]TҦJ\nzjidO+xe{=a#w"/Mſc/@}ffOrvW{TXY1VZނ[&6j,$'îk85[ٹaq{i'(kT=qϽ|)oJ4 8tQ{,Wwۻ5Y=c*{~vw7K;=o^O$ئ~j;K?sþ2~zOow:? :Fw%RtT:qC^Pf쩐Yh"NWﹴ|Zwx{jŽ={Ad8a]v~|7$fٰ2Yle% ,SﵓwSf!aad"Ng(az`>၌WGÔ<%0#TE=_֏>B1yaGmk]}[,ECK:?41 v@ 禿U[ 7wTg&j˭Yda_:]235X` N2 ^nϯN|#N y~~eȌ!_g%X r- "Xk YZmK7>DTd<4ykJb[`;I!!.xZ4F5n6\-KEY-H$"X3 d%R %Ge<qq>%#1TA>qg:`4ˉV|=r:T`cH@׭᫂Չڞ,~"ązҘ~{f"%'v ?)DPAT*l$x 3 9qX_1A&7XV5d8#Hv {ɓe!3(IzYQ7[[amuXƪK%\UZ-B[rܖ+/F5IeE[4[3>YO==4uN1}?uWX؈.0 @PR뽜#[ +}NtOstؒ+5D n~bΫ-U,kN1<>;竢luT['Uj7լ)Rڿ .smqRL`%!2PD\mqN%J- @ 'K1ߎ3gw%{3! $lm3 {6aĞ|G߂/wƣќ`t[?YCt>Ԙ/WӣiOߖtK5a ll}c7g^<.1)s JIیzSɔYmu֦B .Tw:[^𳒯ц3AT)ʦUa{6E][Vbuz&ft,yvkEC~zQmKB^h! Av[b)e6ʉuކfr7Qܥj3We=6b^;\Wp{9_rVj=OۻOL)txHN;ҽĻf K ^禓ǵr\u@WٔӖu\^~ =^ ogct>ӑr ʊׅ=de {kLZYw/?/q˻>]=/6]|~s6[{Qm2g߽@lt۩:4!)dz2eYVnv4w;c1FiEzWsT28WR_^X\w.yna+QVf?U݄^p6 xN?safMܥoDz'Zf-;`.FcaR4=7cټ^Ȣƫu /m"`1u-eU,h+̧E. .Me꯯dvt [=HɫxTvc=˹aY`~]c08JҲ+٪0HCoZx曮N[ǼZlJHIγoߺ ׶~ݓ-m\#+?Τz.{4ܗ?cg!uS4;s,5tzD1f 4 A,Rݵi](wWG+Yakϝ܏ 9 ǽxfjzV:No{ڗL_;3^>ϛ֒I$ I@@C$AԒAC ;P!!@}y-/zi]Դ쥧i-:ӵ:c9851o6/]4]ԻE,qōE,x$F豨-;E,v;i-;hbZ,qΎubLjKNEYNi"5-:QcZtXŎ,H֋7VkzFctE:Q6,sJ,pvu"X炁^y0d$N#FU0tN0((ЖKO:ZŎoGBZuڸqJ4]lc{)կZ,o["X8>,qK-7hor7Ĉ,c,pō$喝X+YNrGB,prKQіiE,vRӣ;_~՞I^1 G8wqz]Y"b_o~.^.|_ROz.$ RD`XVT@]?AZ4h8+p@oB C@֟⎢`έ ߎyN  cH8< D]eG N=@Cy6`Bv, %7cQrt@ 6A`H$HH}nݍ i:ZNÒ OKkfKR!? H=)蔤D"F]}1*c@RF:S" p #t-x:VXD""&$)Y#)uy1G LHY8|qtY Dd*֔J2 ƪc"?VUR.v m4,' 6q^4i}|88*i"3 JE/.Y""~ HunQ\ Viz||/@&Nnp<2wa$M{Si$xKztr9 TԶ48( EjoxS=X8>K^ _c34A/fd jtϟCI$$@Pi8c1ȁ DnݛwqӌiJt8À@hʓ/p,xtc DD@JeQK38eZ)^-EI\∉@cJr˜w3\33r_G̕>Wy3ӪX*l+IFs')Nuez21 @($n 12Ɣ㾫ji1sVRI=g>J[K0߫*ҙ݇ ѝ'9gLxD!@f ؁(ATO()"]dd0"Cp321 D"A뮺Jwq*NRxya8ak^9 E{L 0SzP ZZ/!x$`zgNS"LťcKrʩI$>$Fpg./_ggY?cMjIhɔBCIo1N {dӀZK-5QX # @vYh~NJq7""$C0@ HW soFff  BAqe[)Ϧ{^!0OQ35b7K,[3Ā$"Ȳqܐ HnfdGrpJrpM1>:}=dA4*pM 5&'9֚Ӡ7&r$РӟtrR, Ddi:Y tAuݣf^q“>Kw%c}CmV l<'e3^5vdet\z9EWQ ڼ-#ǗH ȈtU|%m- d%$D3wܾ|UTorK,&C50$"S.=aA$n٪eyǸB\kC9c^Yթ$g쎖u:c(]7OrQ3ZuY>uy&= rM%'bRozBz6$HԒ{ it;q#ϟKҔӧ# v]/ _EbJO Re)HU( R*Rr8* ڢ5*  $Y$RHEs۳vpǧ_OON5,I& jI9t) ʑQ'DC6=ԚF\zܚ&*rJD婙C ". hPDI)Fvpٜ S&e)$H` :Pv4ʩmtwtrxȷעBlӯWjڰulDjt 0dd}-feӻNRxΕ`HmDEiƙh/8[!ODiw0=Bk;-fd̨@wh^iP"_߳'JRwa:7/O4s1foc>8-[؜MO9pES|t%(4(@ T 1DdAM-BeD2T4u5UmQ8%&' V12㎭:5jݳvRVVjٳM^Jye׹LRԹ5N؎R@8(L(DD8@@-Qc< $ C,-Uax6,.YjLLumlIr̈́ M&!  $f ce DF:ۿ})ZRӯ[VX/)唻ɥgVvek,I-6wI܈d/:}i|h0P*/,J[CF)9ug0Yu`pE&. ]&h`;VV +18beIjϳ&IPܲʷmE%(+@XFy cx RؙR$%3"C`}#`B4pG&jgD"23)["" 0 6C >yt ]B'V3y=ct_Zˠ-jR"Ń E-l6 #/T6A[P"[x=)KqNmtU 7Xd38lvL\$-"AVLPhb2dStڲ%&7nIT*mD{100xcf۾ nʿ~~Q*NJgBm {X+G:) īP " x-4\TY)֋stt7rS78l9BnLN OfdŹ*]Z'u/O ۚЛ4׫DUjLUI'$lMI1mURBMaHa&`.{VD_-ßn\ZL3,p౰;9_tb3䐌phGҊ{zMR{NhA0F)N[Tɏa)FD(8 F=pokMID߳YMRffjIֲgqjqXnf%Y!V %Z  =4=qϔ#*T4+zns7-,ܝeamd(-OZW9"dZM͈|`N >ħU9]eBInKh҃xc@ $#c,Zة$থ,1`rTLR*'JNdP{}qvٳW-K9Ռč;gmVB(x0CIҨ GBI6T7':6:7UN ĒhJsQ*dBIC);SD4 lm.f.Jީ$k-&Ybىbli,oM$9N1 i0ʲI ꭔtO cˤj!"kϜs9Vf-:نφn!įǩN򵵢N9mN53k˜*\vXq},QΘJ[aۅYSt0K Y$ 08`"&hP0k[jeH>EM(5?(ٯ>0k r`[¦}ҽryw`s‰x8T8X@buˍʔajv5tsqju95T3i9inZN;cWEiE4d CzkN޾8aa"EBhLXSPĐ11ǸD0 +`ԛ AIzyJс5akW%"66 oro=F"ō9ZJQr.39,uxBPl ` *" 3}UW8VThlj(,Y1@ŇBP=QlPJ JCTG{BeX3.1Ui9NۯDIW"Ae(ܵeRx6Mv jLԣ,*I!ݢ[U:YN'Ujx)j$0^Μ:qק8N֧adcZ~/kk#=RsRGjͦתK $& .CDĤ`q*6"/  h-i14M]J+Gz&ߨMU9Jf檶TKmZū(;vmO^Ď{O M8qwNVv1ӥP ཬRjr%7a1T'9ͳ`x`XT$ >!Հ́]T 6 pՊ q^zҤ쭠u޷tUp 7wT⭅' Bb"kHX`21F!"ȶ˚3c;IfB0/"+M5lӦ:lt<3ÉE%-HTՕ$!Ug"DI$0D X#EDwcøg o/_EjKR3WQdOXJ#$.:q:,w4}y0,GYqyb|]%ߥ8GY5P|rz{C|T$Kd$uT=|u ::++:k)JOٲ3Y,xDVҷ۔N[b.C\ʜ&x7i MZ=mD͕9\#=fb1Dz=YN$< vD<֥BJVȟ5ZS@f_}$m/Ж3P QџeXti -W3ŽTVV=XSzExX1%%9ˮp3F8 >TQt :G iO$lj@6ׯk9E15dXX:~U鄘</S\*J|X~ߎQ{PXu c+({ds!oL'?BC.֣RtvN/W$I(̺WO94G!]C \UNyIudR:<nn-ICdZzs(tMttEiF]4֩WMuyu\pHms3e)rs8ne73! T4L@J\s3nYs$LmmLBZs?5* ReImJD A qah )0HeRrm ih6-Bzdܡ 6m&bi&(IAm׃2&m mjI&,)4[ 10dM`8qa0{ґ2$L2@1M6Y!m@(mXdapa'D&m@, 5!mAI1&-pmbAIÀ) !m!M 7l BM 0!l4؇$l@MMa$[Ә )l4YT$h!6-9%!6B $ i7Bm 4-4Ka6B $iECm!`6)`8$)a$mb &Zh m6-1 pm@"6iC Lm.! D0- I6Sh b 6i8i mp$IA@-5)m$Ç &d(M44mlI0[a m[D |Q*[L$M6 Cmj! &p 6l0$)I6Bm8l0$im6ЇhBm[h%ۄ a6B$IdmiAmـ (I4!m`n)6M O0D0 &eP!$abM6.p p.4543ad62e4d86337cd1949346f9aec976b847b58-primary.xml.gz000066400000000000000000000016501372027637700364320ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/01/repodataW]6}ϯJ&!#ªlڧʁKp6MȐi }8>6$p+rm[sJ@f*򸥿|~}t`y-'fKKk*!K' o 2UJ!14<;#`b'!!5\:a݄[:+SW nn@rK?EQDʵ]dኲL[iMCt4\kp-3C\oXQ6VynK nDFr!J_IůPpIDnh.D&ӢOf&( DľɊS:|¼O1hg54h!7)Zg.Dc¸m1[`w:Z+JsI?ju:4ZKuGvt+^ErބeSrhE0,6r=XÈ&.ϟ=&gg/^i [4.$Q; _ +`!in?]sQ9oDO"9rp4|z}cJq9 735cd6294df08bdf28e2ba113915ca05a151118e-primary.sqlite.bz2000066400000000000000000000050531372027637700372700ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/01/repodataBZh51AY&SY?mxc ޵` .cCAR 1O"=S ɣ i43H44ziF@ѠM i(:zO(2 GU dC@ #F!i24@ 2@ #@hJz&TdFiښhڍ 0COS=@hh  @4Ah!44ѣC@ɐh 141 F MI SAOdG2"z'4d 442#1@BU8$c`#b05Aede~ SLwU4'+(ƅ)aB"R#d`323Ǔ6Mlm;QdL˾G#8RqgeO4nαP"4pYJ::}NMHc^Χ֚ʗ,7>k ER""""L.8abAB=DQ:[#ykIIo[]:tx11|y,$lɊOS1$L -nŷ!Mk6D$4¼QO^69 u"&y-]sWZ =ٱ.+HR(շLqD%/EΨDj V2k ]{g{_0|d]]ǎtϞbU뵝$FJ"L],*^=(!S:UeD!))j{E]<ʣ-g<̓T00I?4Xg\(y|qU89D(S c2 1k hIZpsg')* $$z%zHp1M:$2|gXPdg}Mp.vN3dʳInnnnj*AYK[Ǐ 7USou6bqPH );P#UVnu~dM6Fnv5LPNF&lz٠.9dn|PN5̼&s0XX8S]b8ї d1 d}fC{as0M~y8_gW#&.cS@<<:\StNhJ K+\d!]ϭ~ Qe梄k]im,]mC (e)JRZe|nlno Plfr) I*уe@5ыmt'Uwm-SQxiz6WݬkB("( Q>ǜDq܈>Q>E(ic n*pDAp"  uUUUUUUUUUUV_TٗR-UqH;oHҝbʍ9{ICgfB0R;w|rQװ,Bffa* GN+ Yc.b;VB8-#wJ=>eF)P6ttPl&MIXڸQm(`Ge8C 35_Ÿ/BLvՊ@q|*d?Cyϼ|a <7LtmJme5K)z%%ul#4x<;I>븽^J(C01*!7,CnRdd\'JDDʕK8k 3VqY\ᎩA4$_G SiYxe˺_N&$r^/P B`#-t`v o1gzU OvJ_,# ]n,Dˀ.أFSS2*nӲ_^D᫾˗:öV&R$pu{]VH$pmI[רWoc[A[Q$8`N2]TQcoWo{{0]u[mu M5f31KIom2VY;K6-FH3"ĥ&I311Ffe)IDCh+FiD#$,@BDH a8977cdaa0b14321d9acfab81ce8a85e869eee32-other.xml.gz000066400000000000000000000014471372027637700365610ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/01/repodataoo6STh?E˩v-d|e/; mHHՇSF<"@ts\G_ߢ x*M{C"&(t@/BT:G0f':d69yCt &5EKwpjozE>i™%(Uʋb9ϲ,kH'g49,87׼ V[ CFEs=;qgY,*N8Z3Wk闍]| q'zwlHE5!* Q+ ϓW1`x{PjL7C;-nΔQw{K9xP!9 nCk,:rj#m^.=GQ ej>K@t/'٥nPA*K>Q JYDBct aBtdW53=zyF]fy(#R;iU}?ק_`c?^r7y%C%×W|]|%ݗK=h+Cquy/-|_d4-}7֧osw/TWKNg u=v7q>ueg-_2~}Os %\} ~Isg_qۧ}×| sK^N?؟ߗ-/0l,K/Y/q^V9_%|< _Q_]!}?Z>/|~ϧ&$gy/?_Z ˟Lj&M$h"~DSD M$пD4@/_$K"=^ 09="53?V~Tn) UN}=MsHU~G+?aa=Oח-}w^Kv>>G/onQꇥzg%ȏ/xϟ=_ӿ67P,G3Z]U*kU/> \./qEA; >/,h}|q#U:!NtJSL ~k[{>|󅿟4EY/h ^v-v-&sXv*,OVvq__rXv(ضۂ'/}}A,/eX඀x:\Bm-~_5ح}}<~4 >~2O]G+׺/X Uݚgr.ěmθwowSƻ,u_UA;`Qa6*WrX{jx,Ghr_zgxXfcWV??( }GXU\L}yCJbGƼV51~W{e}%~[W_>(J%(gx,HK[?./e^Q\Q;Wy~+%~gxo$޿W]Q-(gx̗E|;WuEIw{W-|x+JˮvۣvoM|֠dS<`OSPӠESqXOi)ojn}[Sok0TI$=_~P Ve|_e&wk&|WS=iS'm&庯/eئභ~龾 4xb7Xvl'*xTs}}8Vk?m.ܶ Wk(J'Zk`m{?X w5mlgx__~MEDzxA)utu˟|Par[PǰShmMOon탢 u c񿠿k?JJ YFg4:gt}秃H;no?YX֐Xw5 ~ǂOhYx,s+v+¤ߗֽ`ޝ$"{zx<4Lv躯  >ÖJ n%8P0lX:LK:ġAYCA}}aPe8vd|}uUy+G~/qӦ{C|zo{>뮂ۮ~}}A,M?*p׾x V{ Y Z໅}ro;>cO0߆pCcxX    gU/e`PY}}A,KcP8W͙CAw&6ts :uɻ/?x*_~A; 6 X_8#~ǂoh;x, U ? wB*xOӿE<xaHT@B<OC y3< ~./W X0yZ>k?-xg2ܙ #ǂsh3c~f<?(,6jce/?~kkYSSa=$VS74vE~_e91Ms,117FlB PFPثEI?Egq/TmFx#k3c?E ]%R=mǂ]))oZ%}}A,+~ޯnd) ewL@4+ToAwZl{_uXX ::\}Gv9Tp__ˀq,=b&X:>> G f#Xzlmq_o]ᗫ?E<'#z du,:^kF=~k%ޚE8_'ϚxLyke~}}A, ~gt \/p,IT< P,`< b៪ sz~Pq/lW^s g(>OK)vSRp]a)mXpR0Aqlφ.[/e?XfXEhXjbϴgZ,ijy v,/ Z & -Z C ljyR޽RuR]XYNllx?( !5 Inu|WCɻBht-h b!w lHY :R|[jD@BkȖo寸 o"K<̿ fMInˆs ?>aQPҨO ( ,yoE-nﭦSKx(8{XNTϥsIu}g}*NT8SyO幟? _bЕ!tu]BW!te]BWЕ!tu]BWЕ!tu]BWЕ!tu]BWU!$)!qBAl'8 q"@,'<) q?'viS6;Nic6;͎iS6;͎iS6;͎iS6;͎ic< {>.wdagՄ#"Ϫ" c0ehDVM$iDVM$ajDVM$!K;OL81;NL<1;OL81;OL81;OL81;OL81+_~W;ίw|E+_~W;w~E+_~W;_굜3PVȓzHBYABYo ƒƒƒƒƒƒW^X:( 90} 9(y r9(u 29(q 90m78p ǙS'w<8zq  #( P/A}zHBY"O!eeyC" " " " " " Bzm^jBCq5 !0PjBCa5!8PjBCa5!BӼ!tbB'Љ!trB'!tbB'!tbB'!tbB'!tRpc-#hP 3(~Ϡ=gP A30v9#gP Ay30nΜ,"쬚P zdYDY5@DUy Ƣ,"H Ҫ"4H Ԫ"LH ժdZ yqq}yuymyey]yUyMyE=1=rbzӣ'GNL=1=rbzȉ#'GOL=1=rbzȉ#'GBG!ttBGБ!ttBG!tdBG!tdBG!tdBGQ:UީzNգwSȝGT=r;UީzNգwS蝪GT=z;UީzNգwS蝪GT}ZFЬ@3gVLY33{fFϬw-~Zܵks箅]u}EYe4ZEPʰB N(,QYeZEPƪU eZEPƭU eZEPưU %$;ɴO8񴍓O8O8O8O8񴍓O8񴍓O8񴍓O8񴍓O8񴍓O8񴍓O8$)9IYiy䉤ę䩤乤ɤ٤ )9IYiyĉe@gЙsV>gϙsfB*AIǪ !vX%B%!v;|d A> YBb,ղT+jP?5ӧV̞S+yjOܩ;RfʜS+qjN75 (gbLΟ49&F BҠܞޜϽޚϝޘ}ܖ]ܔ=ܒܐ܎܌܊h98q390us98y98}:83!.T^:PjUց@mU*:PG%ԁi/L{tRڋ^݋lv=nۻ#lϖ{l ۻ+lﶰ=n ۳3lְ={n۳;l=n۳Cl={JeFs]E{U4E{M4D{EDs=Cs5Cs-Bs%BsAsAs 4e\~-?maZ|,?e!X|Ƨ;sjBegDVM1pj"J&дj"P&0j"V&%h;63h#;63h;63h;63h;63h;&E^%^\\ň^ȥ^ȅ^e^E^%^^]= }]=?˥3_~>̗_|>3_~>3_~>3_~>3_ 8|>}o 7|w%?w%?w%>w%?w%>w%?w%?w%>w%?w%>w%?w%>w%?w%>w%?w%>w%?w%>w%?w%>w%?L1br M &AL1bb+^!&CL1#bbK䞈M!t2A_ʕW/r-Ջ^ }rC_ W.zM^}E_0W-ilˇŶ|Il-öz )lˇ| l|kˇ|kϿ>?;EΟH D?U)1xPOdSe<(!xPO*A ?Uƃ*%x>3.w`frՄѳ"㪉"c0foDVM$!nDVM$lDVM$"#~*W}. r'OeSuYp' Oe˂+p?QW. N|hbVZ1P+hb(VŠW9*\`fbhUT9* /vȁUBOJ B_Agd Ad Ad A'd Agd Ad AڎBx*AIi !n?^%B܁%!Aw!g A ĝYB^,!%ZJ JJJJ J J J J J J Z2j%P?f S3zj%O੕;5cV ZS3rj%N75flhf8q6 3g0u6sg8y6g8}6g863hiCg!3zPBCg!;zPBCg!;zP+kAe̟V2}ZOi=eʝ*uZNi9eʛq2msW]-Z?w|jsW箖]Z>w~jsW箖]Z>wr_RUBOJWV!*Eb,!J%KB쿒%!_d A0Bj*AI !\%Bl%!af A0 vYBb;,!bN^ t !Juxwxwx wxwxwxwlC[p[omն[j[nmuvٖ[d[omז[^[osmv֖[X[o[mUԖ[R[6y_y"\ S'W.}ޏ#C:: C::#C:2#C:2#C:2#CHZR[oIm%[R[nIm%[R[oIm%Ԗ[R[oIm%Ԗ[R[oIm%Ԗ[R[oIm%bЕ!tu]BW!te]BWЕ!tu]BWЕ!tu]BWЕ!tu]BWU!4li1C!eE бf=$'ƒw~5ks]f>w~5ks箙]f>w~5ks"kAha-̟E0}Y=ga,ʝ(u΢Y9ga,ʛq0m^'.wdagՄ#"Ϫ" c0ehDVM$iDVM$ajDVM$!K̤7^Lzq3ͤ6^Lza3ͤ7^Lzq3ͤ7^Lzq3ͤ7^Lzq3ͤ7^LZfTS}n*M<4gTSy^Ki>,gTRyNI)IBɳBBBC3CSCsCɓCɳCCCD3DSDsDDɳDDDE3ESEgY:+ 393} 39+y r9+u 29+q ̸9+m ȍmM- ǭǍmMzJhC~BhhhhhhhC7:lUЃ!`ƒux*E!`YB!`YB!`YB!`YB!`YB!`YBbx@zR*<AV.ze A ^YB⎯,!qW+KB/vigUГ\Ϫ !N]!d As>YBO8%!d As?Y"߱fJT3jO>5fJS+wjNԩ:2fJS+ojMʹyrgNvVM(=,"H Ъ<cQVM$AiDVM$jDVM$jD^d=D &Ȑ5ǀz YY dʚH CVD&Ȑ5@2d}&ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&{N2`0z~eT`Tb(#######3zZ0dCg3zVϰT=Cw@ 3zR0D=C;FB#Cht !4:FBChd!4:FBChT5R{xeõ^6^{pe㵗 ^6^{xeõ^6\{xeõ^6\{xeõ^6\{xeõ-fu|'NP;wB: u|'NP;wB: u|'NP; D  PVxPV'=$      Yg`v-f`fmf`v'f `v7f`vG-f@l+ֺr[m+ֺb[m+ֺr[m+ֺr[m+ֺr[m+ֺJJJJJJJJJJJJJJJJJJJJJJJJ-v !48BChp !40BChp !40Рݮ+ubJݮ+vbݮ+uRݮ+uRݮ+uRݮ+u0O辘ysՉB"DU "Թ\UKsBY"p,\yP~<`(K@W1%+M2nJ)~nL1znJܹ1vnJC̹1rnJ禼1nnH\ZiPmC3z(6Y!6Y[hPmCmCmCmCmCmCmvPgx_̼i:UA:;3\%<;3Aώ D:,0K@;3AЎ D Ԁ}1CQU"OQUAB ]]ݮD ԰uvPnY"j:KB ]g@%> @yR-YD tUK t,'KB.ЁK|D t,'KBF@6@&@@@??????SO.wdagՄ#"Ϫ" c0ehDVM$iDVM$ajDVM$!w\gXwTwPgLwHgDw@wb pU`X0GU,7,"P znYDP[5@Uy ,"H ت"H ת"tH ֪2d . !2deM(=Ȑ5@<DCdʚH CVD&Ȑ5@2deM$!wd=D &Ȑ5ǀz YY dʚH CVD&Ȑ5@2d?qyY!+kB=D2deM1 "CVD&Ȑ5@2deM$!+k" Y3. !2deM(=Ȑ5@<DCdʚH CVD&Ȑ5@2deM$! LWj:*AY (6_%Nl:%&6g Mdz`ĦYB b,M4)&?gRL̞3)y&Ϥܙ;RgbLʜ3)q&Τ7fLEi0m͢Y6fa,LEi0m´Y6 fQ,LEi0m´Y6O;sjBegDVM1pj"J&дj"P&0j"V&Ȑ<Ȑ5@"CVD&Y!+k" YY dʚH CVD&Ȑ5@pyY]YE]YE]YE]Y]Y]Y]Y]Y]Y]YeWU`*r0|8 _"WU`*p0|9  _EWU`*p0|9  ]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]W<Ȑ5@"CVD&Y!+k" YY dʚH CVD&Ȑ5@,"~yY\EQ O?j"Y^EQyu,"_E,"_E,"_E,"_E,"_E,"_E. !2deM(=Ȑ5@<DCdʚH CVD&Ȑ5@2deM$!qyY!+kB=D2deM1 "CVD&Ȑ5@2deM$!+k" YpyY!+kB=D2deM1 "CVD&Ȑ5@2deM$!+k" Y=. !2deM(=Ȑ5@<DCdʚH CVD&Ȑ5@2deM$!뭸{~>'xyJ>'Ÿ^Μ,"쬚P zdYDY5@DUy Ƣ,"H Ҫ"4H Ԫ"LH ժd%Y˰JJĘJ̨JLhJ̥PJJ8Jʢ JJJʟIJIʜILOHOHOHOHOHOHOHOHOHOHOHO   º$º,ú4ú<ĺDĺLźTź\ݳU=@, OpO<@,,@,,@,,DCΜ,"쬚P zdYDY5@DUy Ƣ,"H Ҫ"4H Ԫ"LH ժ"dbȰUjdL5:FpM#it, ё42F0E#ht !6/viѪ'%Gƒh"qQ(KB\|%!.? e AKD h1vg1{vg)wvRg)svg)ovҦLNi1m:M6ҦctLNi1m:MǴ6ӦStLNi1m:MǴ6O߂eΜ,"쬚P zdYDY5@DUy Ƣ,"H Ҫ"4H Ԫ"LH ժ"dq3zGUSCgD3zGS`K=Cw$ q3zGQ C=C<B*AIiǪ !X%B%!n7}d A> čYB֏,e-#h3̟g+|̞=g3xʝ=cg3tʜ=#g3pʛ=f7mgc-;iƎ5vڮn7kWcǭ;qN4vܦ.7iGc-;qƎ3&['/?pÉ'/?p'.?p'.?p'.?pY2f%?gf 33zf%;3cgV Y33rf%73ff<>,wdagՄ#"Ϫ" c0ehDVM$iDVM$ajDVM$!M(=W:>M$!AyuW<>ȐG~^ȏ"C+Qdz#? Yx|G" . MYo"zA֛D&7d MYo"z@֛&7d MYo"z@֛8-|ԄSC>j" Yߺ{GQuUdCV![wYEo=duUdCV!kŎa58R#jp@ 48XJ#i` a48BR4{EKW-^4sK3W.^4{KW/\4{LW0\4{ ELW1\4{̅L3 PG($J@) ^N'@Ej PUe/,TxiBB Chq-!8ZB Chq- !0ZB Chq- !(섒tU]@WUse\>WfϕsU\@9 r !gUBOJYƒJu%!>ne A\YB#WЕ%!>ve@h#;6Nh; 6Nh;6Nh;6Nh;6Nh;6d-#RU S)~*O=RT JS1v*NЩ9#RT JyS1n*M ̛Z;.VE A j|@P5< A j|@PFb9?a8x;Ca8p7?rgNvVM(=,"H Ъ<cQVM$AiDVM$jDVM$jD 3UVȳzHSYAQYoƒƒƒƒƒƒh-#HG 5XVlxb`ņ+6X; hUГR+UABlJu (KBl%![e A V@YB:bЉ!trB'!tbB'Љ!trB'Љ!trB'Љ!trB'I!4oh1C!eE ٓ=IɓÖy[UBJ<ƒux˳Juxx˓%d §bTJS1x*Nة:CRTJS1p*MŸ6LZiS3mjM6ҦfLZiS3mjMʹ65ӦVLZiS3mjMʹ6e4Ji0mMôi6 Ӧa4Ji(mMi6Ҧa4Ji(mM -;p¹c N-;p¹c -;p¹S -;p¹S -;p¹S -;ps_̼٫cϪWӞUA:{Y%<{AϞ{Dg,'K@{AОM|D t:090{9(wR9(s9(oҦ-O[Lvy cq\"Ϊ GEğU DZ5`,"Ѫ"(H BӪ"@H Ԫ"XH C&ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&HӾ:鴯}ugfϙsV§a4F0xNi: CQ4F0pMøi6^rAPA\zAPA^rAPA-^rAPA-^rAPA-^rAPbPBCcu!9:PBCsu !1:PBCsu !p/v Ǫ'.ƒ؅c" G؅#KB‘%!vpd A]8D $!6wA =ox w?o}|w==oyiW6{^ig6{͞iW6{͞iW6{͞iW6{͞iWd-栴90mL9(mL栴90mJ栴90mJ栴90mJ栴e-ӦWLi+mzM6=ӦWL^i3mzMϴ6=ӦWL^i3mzqywqypq wpqoywoynywnymywmylywl b1h n1h n1h l1h l1h l14*F?#gT̞3*yFϨ;Rgdʜ3*qFΨ7Fs6Ks2 s.r*Njr&Kr" rqqKq qp'\"Ϊ GEğU DZ5`,"Ѫ"(H BӪ"@H Ԫ"XH B\O~>7_O|>%ķ_O~>E7_O~>e_O~>7_O~>bZBCha-!8ZB Chq- !0ZB Chq- cMB@(+AI=$ 7OzHBYABYABYABYABYABYABꖪꆪvfVF6&:0هYՖ&$~- LT[$0ASmI`A.lGK?Yϕ~S*LGJ?Qϓ|$&,GI?IAϑ|Lbm(QrwF%v8Jnq(Qr6G}%w:Jlu(QrvG-vB'!tbB'!trB'Љ!trB'Љ!trB'Љ!trBgY:+ 393} 39+y r9+u 29+q ̸9+m.isQ\6Eisa\6ͅisQ\6ͅisQ\6ͅisQ\6ͅisQ\e-檴2mLʴ*mL+檴2mJ+檴2mJ+檴2mJ+抴a|#{8:niy|ϣ{4^K>h%v;g ! $7X`M|5 7X`M~5 7X`M|5 7X`M|5 7X`M|5 7X`M|;=ӳN;ZӫNϾ;=ӳN;=[ӳN~;=ӳN^;=[Ӄi:pڳi=;pڳi:pګi:pګi:pګi:Iѓ'nHOޑ%=qOz]ғ'nLOޙ5=yozӓ'nPOޡE=yz&$U;v$/I^t'yN;w$/I\'q O"Uɋ|W$/I^瓸'yOR>ɋ}W$/I\'qO5?ɋ~W$/I\'qO!*AIUAB%-vB!t`B!tpBЁ!tpBЁ!tpBЁ!tpB!5"0 BU⯋ 5?!a!!4:FBCht!S'1a@n 3 2l0. 2=:0 82@#09:P p9:` Ȱ9:p 9: !a/vi '5̫ƒy"q s9KB\Ü%!a0g AkD Th1C!eE [ϖul@['ЖumB!42FBChd!42FB#Cht !42FB#CH;/vعb΋;.vعb΋;/vعb王;/vعb王;/vعb王;/vY//½//}/]/=//..삽.ꂝ.rumr'UInS)Y,)*ɮTC$Ѝ@&CC{yK5 4UY{mz׭~z~zz7zWzwzᗮzᷮz׮zY2f%?gf 33zf%;3cgV Y33rf%73fVZMPM`M`M`MPMPMPMPMPMPM`z_׃~]u=z_]u=z_]QRSTUVWXYZ[\]^_`abcdefg`&bdo"t&{Sxdo]!^%!^%!^%!^%!^%!^%!a# r0~> ga8v:f#a8n6u=z_ףG~]u=zȯ_#G]u=zȯ_#Gq5Gw5Gv5Gw5Gw5Gv5Gw5Gv5Gw5Gw5Gv5Gw5Gv5Gw5Gv5Gw5Gv5Gw5Gv5Gw5Gv5Gw5Gv5Kxgrt'zp*zl*zhgrd'r`r\rXgrT'rPrLx|n7;᭎t|.79-p|77پrgNv}*"4MO!P|cQAiz iz iz vY˰ڕT;cjgF +vPڕH;hW ])3vٕ<;cgW ]i+{W=^ٛ2wyeo畽+{WV^w{en+{W斯=_w}enʶ!B!B!B!B!B!B!B!B!B!B!Br3j:tәOg~C:uK̷ٯ3Sg|S:]u̷ٯ3W9?>??>?>??>?>?>?>?>?>?>q7w6w7v7v7 v   t(9?P̞s(yϡ9;R`ʜs(qΡ97˝9UDiBɪ4=BMEUD8jB)DhjB)DjB)DȲŎ p ` P @ 0 bН!twBw!tgBwН!twBwН!twBwН!twBw!d"]/].{ z@**@**@**Dz[ࠦ(BJ‹u88Iwx88J=T%U AUB!Mn"45)ASAS ĩUB*!qjt85JB]%!:VZB+Chu !:VZBChe!:VZBChU=ZFУyT<2}>̞GFϣyd<*u:ʜGFΣ瑁yd<2mrgNv}*"4MO!P|cQAiz iz iz d-IIĘzbF=)NO'CI8zR=1BO'IyR<1p6Ey]MQ>v7!p}"p}zJ>E_%wW ! ?fÀ]Y̾^>ª XU} \U} `U} dU} hU} lU} pm}ުD}UJؚ[ ֪ʨD]iMUʈD]gMUhD]eMUJH6Z&ؠ\SkPb LAI50&ԠtLRi`" JI4(&РS?T@TAE!4ZFШ@#gTLQ32{FFϨٓ=Iɓ>#s>2>#̳><#><#ȏ3?<#؏s?`})Aqx} ox}Amt}Akt}Ait}i/._]x|v o/._]x|v勷n/._]x|vY:@`s(|f9<PCs0te98PibY8#ga,8vY< gq, 0~Z@ #hq- ![b>} 3Ã} 3Ã} ~?~?~?~?~?~DBPUt/.UU BPUxPUxPUxPUxPUxPUxPWȏ`Q"?E~+Wׯȏ_Q߾"?}E}+WWȏ^Q߼"?yE}+>Zͳi̴yV<3m6Lgͳiyf<+m6Jgͳiyf<+m+kWǮo]tE~++WGo\Q"pE}y+Vǭo[Q"lE}؊6m4jhmQF#Fm6m4jhѨmFFn6m4jhѨmFM2m6ƴ٘6fclJi1m6ƴٔ6fSlLMi1m6ƴٔ6fcӷpECӦ"Dp$<n:x Nr9qt`;8z:頏㧃?9:9oB:#):З9MEB<#IwxFNxFNxFNxFNxFNxFNfZBChv !43fBChf!4;fBChf!!567EGRmn /Bb{s"ùJBq >*!ӹJBu{_B+Chu!:VZB+Chu !2VZB+Chu !22-vePvePfePfePvePvePvePvePvePvePve A AMQDԠ" AMK 4!JBD 4!JBD 4!JBD t( F5EGRWk /"rX]9JJBWЕĪD t(*]9JJBW-visSt/MEB$B\%!Nv';W A UBd*FA ]ox[ ?o~|[=]oz|[@nr/[ F  ]@nb[ ?na[3q3;MU>YQiz>GMS4=MMS4=SMS`4=Y3" E~'N(P;wB" E|'N(P;wB" E|'N(P X7&o`M5qk X7&n`M5yk X7&n`M5O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(ORWN^55zSWM^95zSWN^55zUSWN^55zUSWN^55zUSWN^55ze-fWLi+mvͮٙ6;fWL]i3mvδٕ6;fWL]i3mvͷ;s4]Uiz 3p4=JMS4=PMS04=VMSȐz}w!jz 3 E)dȪB)dȪB)dȪB?O2gUg1~dϪbϪ"$}V}V}V}V}V}VAȍ#En(r@4Ni"7 qȍDn$r 8F)"7iȍ#Dn r8>!+oECU 0)P)//ܩOSx@OSxOSxOSxPSx@PSx{0a9&sL81yc0a9&sL<1qc0a9&sL-v%Pr%Pb%Pb%Pr%Pr%Pr%Pr%Pr%Pr%Pr%D'NO8W?q~TġI3G'NO8O?i~4aF'NO8G?i~!2m36qclؤmb6&M#6qCl~Ia7&M 6q#l>mI`ϸ̛ϼ[ϼϼθ̛θ[θθ͸̛͸[͸,E[ў(BJ{‹uIwxJ=s_%W {=UB:ot_DӉ|V'N{;ov_Dۉ~v'NfυsQ\<΅sQ\:e΅sQ\8ͅqsabf5EGP?^ dM⿋!dUB*!pY~8J@FV% # -vi$YSt/dMEBL$BMV%!N'U Aʪ eUB┲*3ڍv[hD# v;4 ^hBڍv4A2m6f` Ji30m6fP LAi30m6f`ڈpp>} 3g8pp>} 3g8pp>} 3g8p`>} 3g8p`>Á} 3g8p`>Á} 4*F?#gT̞3*yFϨ;Rgdʜ3*qFΨ7#F}F9g~F=g䐟S~F9g~F9g~F9gF9g!n3gt->[|Fn3gd->[|Fn3gd3 C2+C: C: C: C: !|tQ8pbO] 'Nl,Y8pbo‰݅ 'Nn0a8pb&É] 'NZ1ykĭVLZ1qkVLZ1ykĭVLZ1ykĭVLZ1ykĭVLZ1ykĭjL1qjL1yĹjL1qjL1qjL1qjL1qƋe(^@/̟ Eya(y^t =]|sO{Ӆ.>tṧ=]xsO{Ӆ.>tṧ=]x)e6wA =ox w?o}|w==oytbo܋#x7Fv2wdoܓ)#pWF}3wfoܛ9#pwF]= }]=%v>z|ѣ#gG>=~>z| #gGB9>z} IgG!=oz_D~'uO>o|"_D|'O{?o~"_D|s1n1{n)wnRй)sn)on?;s4]Uiz 3p4=JMS4=PMS04=VMSHd-*)c*1*1)1C))()()#()')yc')s'1m~Ν2χ}2bчQCAOeU_1ՇCW+F0P#}Aw୪nMW>^UښbjMUʘD]hMUxD]fMUXD]d/SG-+e~hv_($—r ~h_(\$BYf'2/?IPIO/O_~N~:??N~ {)D?N<:AD?cӻڝEt/;NREk'Q{A^lvҲlEb' {ǽo .wUi ч"\"5Mg0Uaiz Кiz Ěiz !;|oֱ-ؑfc[W\-iul|ձ]UdžR(Aul|D&:|vF;wuXkap۹ݮo熻ǝ[:{f3Ϟxg6=왍g첖+v?;gW̞ѳ+yvϮ;Rggʜ+qvή7&ZMVdMfdMfdMfdMVdMVdMVdMVdMVdMVdMfHqT̩SgN5Ξj98{qT̩S3gO5Μj=8sqT̩S3gO5ΜjR9rk/3,gZ|r9r˙/0gb~ǜ9-sk̙/4gjǿ:^BW+C!zu:^BWЫC!ze:^BWЫCU!t3;3{+wRй+s+o;_;s4]Uiz 3p4=JMS4=PMS04=VMSHUd-ê( c000 C( ( ( #( ç(y c(s (m -f޼K8UQD>ԻDS^DS'KUE?UE@UEAUEBuxm_}upm_}uxm_}uxm_}uxm_}uxסm_;a܆kM;`ڂsƮ ;_~k^zk΍]vkM\rk0b8#gbL39v&<grL 31~&@#hrM !A 40Ϡ>g` A30x:CgP A30p6 -v, ʼn8rfY:cgq, ɳ0zgY> gq, 0g!jn")AA WUB*!UsJB|\%!x[č(B_JC7‹8wIw8zJBQ%!ߨpT A#8 !U"e-#hT 3*~FϨ=#gT Q32vF9##gT Qy32nFU2mJ+ʴ*mLʴ2mJ+檴2mJ+檴2mJ+檴2mJy\u~ZuXwuVWuT7u~Ru~Pt~Nt~Lt~Jt~Hwt~F(5Yj䧩ɏS&?PM|H5jC䧪U&>XM~h5j䧫W&>`M!MQk4!qF"qF8eJBQ%!NڨgmT A6D Q;'s1k;'s;sѪ;'sꮱ;̴y33pf'ș9Cgf̎ٹ33xf'虝=3gv̌33f'ABHb9?a8x;Ca8p7>KO鳿>k鳿>O3>3>O3>,볿> O3>+쳿>KO>k>ONh;wB -Zx'Nh;wB  -Z|'Nh;wB  - i6i6Qi6Qi6i6i6i6i6i6i6i6jjjjjjjjjjjjjjjjjjjjjjj^y敯Wj^yկWj^y敯Wj^y敯Wj^y敯Wj^y敯%280<.<,<*̓8(s8&S8$38"8 88lLMi1m6ͦ٘6fclLMi1m6ƴٔ6fSlLMi1m6ƴ٘6rkX6>`m~kX6?`m|kX$P9 ճP= P=sP=SP93P9P9O9O9ԳO9ԓO94s(2ϡ>"s(ϡ<"s(2ϡ>"s(2ϡ>"s(2ϡ>"s(2ϡ}u !:VZBChu !2VZB+Chu !2VWWWWWWWWXX#X3XCXSXcXsXXXXXXX]4q h=E1wѸsݓ4q,i9MqwӸ{Ɲ5qLj9Uw(T@T?ES>ES=SmY37gaž=s{Y37g]-v<0p8f΃C灩yp<0x<gyp<0@gЃB&reȕu"W\'reȕy"W։\'reȕu"W\Y'reȕu"W\Y'reȕu"W\Y'reȕu"W~˝9UDiBɪ4=BMEUD8jB)DhjB)DjB)$Zi1]""%"<]"p /"` /"P /"@ /"0 /" /" O!.UEGRPUxPU_ .UUUUUUAaì1c3f9s50saì1cf9s90kaì1cf9sӷ?+.wTai '"O" 5Mg0Uiz iz aiz  딵 SIu2Nfԩ:Nd. St2Ne :B'#T Ss2vNe96 wCY>wCW Ƨ2ԇCW+FP#}>T}ŌW#M<]D,5BQE ?(?`AeQE ?(4Px m²JMQX ‹*555h5H5(5 4(?gP ̞3(yϠ;Rg` ʜ3(qΠ7FvWϕse\f)Y~#Bbͪ]MQD>Ԫ]ME:+w5.]U"YJ>+wUg*U% tBK-~rK-'~rK-~jK-~jK-~jK-r|wʧ)~|w')~|wʧ)||wʧ)||wʧ)||w# 3PEK=)@ MK Ȇ*=JBl(# D Ȇ*=JBuz#ɍ\7rur#\'7ruz#ɍ\7ruz#ɍ\7ruz#ɍ\7ruz#ɍ\'Ov5j>|TWɮS]'Ov5j>|dW]ͧOv5j>|dW?DsP9~~!JY/G!G-/|)ZB/_(Kx e ~,a/%B_HPV܅v=b^(ׅud]aWzhՅ>uH](rz}w!jz 3 E)dȪB)dȪB)dȪB7j:XIi,/i_Y]HY}d .p q]t[zpE. ]=V0C.8p I=42(ku<ے_gS$7[~܎aO O?[a%Of^c_!~77ah}op7 fd96942c919628895187778633001cff61e872b8-other.sqlite.bz2000066400000000000000000000025771372027637700363720ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/01/repodataBZh51AY&SYJ'kTP!BP`{aCTF0IM҇I2iF M4hbi2A*F2OM#DIi2 4AɀF24`&0 Fa12`&Ѧ##L Ld`#р(5=#Hm' = i  m1*Ow5H !3? ;QQMUZ!ؕaWc>~+PBQ8`~ujq[k)˫ȋEEk%FQsyR:N2A;0 RNb"ҨukKjRAkK.үF_cްXpWzi"~ +F3+=6]z!3`6JIg 4dqfLYecA(2n͙U;c|$DW9nIS`02(aRa.ux(~8J VvSLI̪\7*5 Ah0ِ v0#4k*GV"Q)œU;&|F,J/H:SIaZXE7وgqD. y[Zna'G˫um .fT^u%db c=DK$TH@Y1~ZtV5"&vuɂr+P ciK d, @xޮ(rN)cgH'!b=Al[/Ñɕ\dz0ؒb\An' ^1upSDVMEuRdl窳LtUQ4MNkL{YQ$k'uD"Ơa5d@qB q !U [NAOL&jY"[( mY321@BS}!# ç{(+
(q LB̎9ʔ́nʊpl@bBW )ET.5 $J5b^[d[tY@a+L V!U%2'R:Q;cKAʥb &t%7.g"V65H!KX,?40/' ߝ&D*oٔª%yg d*]|+&b!šgP"X)Bhq R%^ ٢y|n82FmȜ&erC@4c.p!librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/01/repodata/repomd.xml000066400000000000000000000050751372027637700304230ustar00rootroot00000000000000 1347459931 4543ad62e4d86337cd1949346f9aec976b847b58 68457ceb8e20bda004d46e0a4dfa4a69ce71db48 1347459930 936 3385 aeca08fccd3c1ab831e1df1a62711a44ba1922c9 52d30ae3162ca863c63c345ffdb7f0e10c1414a5 1347459930 43310 735088 a8977cdaa0b14321d9acfab81ce8a85e869eee32 4b5b8874fb233a626b03b3260a1aa08dce90e81a 1347459930 807 1910 735cd6294df08bdf28e2ba113915ca05a151118e ba636386312e1b597fc4feb182d04c059b2a77d5 1347459931 2603 23552 10 4034dcea76c94d3f7a9616779539a4ea8cac288f 949c6b7b605b2bc66852630c841a5003603ca5b2 1347459931 22575 201728 10 fd96942c919628895187778633001cff61e872b8 c5262f62b6b3360722b9b2fb5d0a9335d0a51112 1347459931 1407 8192 10 librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/01/repodata/repomd.xml.asc000066400000000000000000000007521372027637700311650ustar00rootroot00000000000000-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQEcBAABAgAGBQJRD7FVAAoJEEavlYoi8sTp7ekH/0PFXPfoJliTRhChb0lvS+mG AMVj1SJe9T90QQjGQpvEUrXaCtyBORnnDxDXFJ6xIiCiojOS29JXGHx8UkDniN/M cNT/Rkhin/eFida3pt3ry41LEjlCNzHXka5diw71uShlSa4cOmidCHRx1fQqJDj0 igbEWqr6q2AkMGrYq40XO7uuhUpG1B2pAwX6dV85rNv5cI6QjGulSlm2Cha8msol UwHjE9bPzprpjJAUjoXPSkY8roYC7Q9oj/FtTSOG1HAvvoO/SmDEsr9xGgRKlF+B oxfhr4Ti0G0NZdIq8Dmg15V7wz+YpW9GyfRA4u8GhHBQ/0NYzoGaItLpp5MFTr8= =1p8T -----END PGP SIGNATURE----- librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/01/repodata/repomd.xml.asc.bad000066400000000000000000000007521372027637700317120ustar00rootroot00000000000000-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQEcBAABAgAGBQJRD8GyAAoJEEavlYoi8sTpiMcH+gMpiwJN8Oe3kGm21eXlacFI 1rh7yRJiWnuxqHYYiKguojsVKKqooHHmJfGvF8zRvZGy+OPnW+n+mRCotRTsSt/z XeQP4zIEzPgFe3+qxJoCeL0Whln0pc9qtJ4AfNaUrDydUH8CD8+bBCPoytRlqjf0 3pOqgyz9D5dN2rWIAHOQh4Vg9hd5wExtd+XMA+PmIRhzFB3G1OqRP9f0DbXw+5Dt uTdPy62SusJTvEfQ02H+t+kQoqLK3kzeHdnblrjHwKAeEYkgMHq3a0XbwrpMQaQN JGAVnusftCponchmN3ArtGygjauu1L3zfzlBJrrCpcWJxWID/jTA5Y66SkJ0H8c= =u0fF -----END PGP SIGNATURE----- librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/01/repodata/repomd.xml.key000066400000000000000000000033071372027637700312060ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.12 (GNU/Linux) mQENBFBXDKwBCADA5jpCwpb/JKOG8mcFyIanNojDwpHwKoyjGNpZNPNUDJguvkRa IO3NdoyXYd5QVTOsnyKBaRaiLLJWI/VJxTOT3fwOPprrzUlkHwoWl+sYuSdXHASu m4lkBiXHsa5oiXPdrY6hoh5vsF8ASwCHXOwpR9yyvGEaUUMBl2GpJAX/cGVcL4Dy Z0pyJMLO4qrIPoX+wd1ZSFSc8JcAC4UtA82HCGTmesgialpwKdoQyt+em94oIM1f D6v7zzcRX/zLKKEzpFnU458WBA+JACkde3ohX//0fDCeaLqMzs++FCgwm/HMCszw RnINr+K8ENfMYBoeM7a7tnhiae+rkxWmvWz/ABEBAAG0LFRvbWFzIE1sY29jaCAo dGVzdCBrZXkpIDx0bWxjb2NoQHJlZGhhdC5jb20+iQE4BBMBAgAiBQJQVwysAhsD BgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRBGr5WKIvLE6fdyB/9OzDczaqGy 1rzk7Wp2C9S5QatFUFNWt6FIFPITbixT4jrDo/LyUJVWLw4ng7ldg79vmrzhpP8h yFVvuvGvSEMn5sgnZ83SEd4vRJ2O8K5RuVs5Kcj7ayLlxPpqbYOYmrmTaLwYTwdv 0wDnNU9IkkMSK752RQes4J+4XGikd8CNm5lw8cRQ7bcQd8s2rnCoiyGt7PIdl13z 8hO9KA52iUP06AbbIusbQ1jzsViEny+xQH7SZ53Ga4eRr0mW2iA20Mkp4Ieb+dNo 47Q8aHUqI9O4HTF/3Fzt7KmNxXCpCOhxTWx0IkqPGoZ0W63Aut/CVh0LXsBF2TUD Ym9P/IjRgJLhuQENBFBXDKwBCACvhlMcgjLJ4PtMTtauF1OXVTfODQSHo+qwKt4S GyDlTGayQ76pOqYkkzIRqmNYl1ThmcfzpmJ3O+hFQQ7OdguYcfkbfgIMjEJEbKG3 wsR5pm9zjwStzYHedwkct1nyROgBz70o16FfdiWOguw58jQZOSO/I2S3JpLsLgI8 KqdIk/0WuoOfzt+KcvL52lX94O2hBpRI0v6lDgSm7KkPGQrVFnSIUR5r73ceageL 5LmGm1TlEjWHwA9iYIvBcjnE26/l8u58IYQ/sUmn0u4jBcBNc7iqdWvlSLZLlMmi qnzDNhUup9neKGxgr4hGAblxiSxXlmOoFv0jEW81b4VximSJABEBAAGJAR8EGAEC AAkFAlBXDKwCGwwACgkQRq+ViiLyxOnf6Qf+PIG//12qp3hXZsvB7JQuQ4nUNwp6 Ufm6W9pFm3DOqnI9H9ZNzGbkoS5WwRp0B1NLfNKipQVORnDs6qve298ReRrmLKnk BPZqxFpPqLQ6X83Or2bqKiJS1axonIgqkImFLfxxqKoukvhn328Z2FVlrvkKSMU8 eHi/iDF/TCHoPE9WtnVSzsNU9i+9j8j//GO+bMC5AGNOxcBKlChFpLYpE/pfITL/ icS7wB9MrMLNvjlN1EKszQFxJrFVBGTt8hUqRH3CCUFRwbpE1QJ1WAzJ0Vzk5nWR rVZQiiLe03B8hC7/qRiB4bya5nbWcwe9ltPFja4/tTe92ivScFfCLyALVQ== =G300 -----END PGP PUBLIC KEY BLOCK----- librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/02/000077500000000000000000000000001372027637700246065ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/02/repodata/000077500000000000000000000000001372027637700264055ustar00rootroot000000000000002431efa18b5de6bfddb87da2a526362108226752d46ef3a298cd4bf39ba16b1d-filelists.xml.gz000066400000000000000000001245121372027637700424760ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/02/repodata]#I)c@u53tueKTfJ"2RIK/UǹƘrIEx?o\K7??Tuc/ׯ_ۮ[NO_˾VPҗC}zO_~\OsS5?O~//g~?_m_/(|Xҏ/ _> lK?w}unóo}@?D"eD /M$Я"Hh"~D&z|,^¼^E@ܾi}=.zH6#t3Oi^(pt4w/TuOt}~l(p*[m_~_X~ɓ?X忿z/&G㑶 uAVcUǪ Z ߭?W_k "=]Q?? 7 [$?X^IC_2zw> "=+U1|6ϥLx#X@Ϧz ߭/¹S8'c.d|"/uX/<|\^ RSHx}ԩ` j.K{@TpHOOWmϵ6/L| 'X/} .o vQlW>_kS>qyDXv /e[x,;mM?"68fRXw'/(F`'(`>7>Ϯ(c/&~ Ljy >︑*GN:%| &ANW5N=m nwqO bk烢,Ǘmi ^v-v-&sXv*,OVvq__rXv(ضۂ'/}}A,/eX඀x:\Bm-~_5ح}}<~4 >~2O]G+׺/X Uݚgr.ěmθw { wUΣl~UͯlQ6oXVVj㿜ξX=ͺDz߭)~PlcOW/r=n3ӧ q>+[߭.>k b>o( ?(J(g˿|P[@Qv'X闶\^$ޫwEQo\Q竢W(Pe>(J⽱Jx\QrEI{(e>(1_/\Qo+J߻n v裏ξUYBSĿ 7K|6$.$g](g]A=;vj;+k,0VquO_ۯEAˎŭ]U T:ht2]<>3>=,GFat{ɺDzwN>|G˚c៫௅}X]AV3]Lg&];7%dx',ۇ~[WerC}}<@9нe tVPp+yxxgmIJС`Z$̂ |rO?n=[;d ޕ8 `x^,`#C%Rߨ[ ~ܧdx,t<8gWo74 ,a#}}<~ ><>73(_ΡA,Qt_e'C ':pKw{{$=b}A..68{c=6^wvl bo*ُUKdo}׾GoOJ߫_TJJ-V {1S~wmp11gٺ/e߰Nx@;znxZXm IJ}}<Z*~ ؜9tgbCWA?׀x*SgX ,g(|?GP?`SAAOCA}0x,P%^k:Pp'4~o><9}PYa L$Pp< >K3R{!~0\?\Q?%PñF;.ˆw&cɈ 1,8v?3gӟq"kc<6VX渖5%1uCh%=}CcmWX94ǂ>cA~c& e Oz(~P9P|PFgs^pF'2Ofti;>¿ Q hX K[󙖂Lx6oeBKdCKAvh_-4^ ܻ] ] kY5 ©M_lEA*xr~uE|P٭H劉kA$IՑ'9܊lGtNM=dSO}. bُ}}<N.O;~ >84T05O}}<'p*pP%|Ty{BñS :P9p ֩ u‚SA  ~pNx%w*;~=88a֩FNNg!9)?e"=Ω]'Uy28V\ LowE<NJd)k槷xIYSs{G±Ϙ!:dΖ!:3Dg|Sb?3=/۾m_RtKA/n]߭x_xoL}}A,;] FkhSs=ñnF[7T >}㱐 ތ8/nӫ??;|P㊟k%(+{q5yogޛUU17tD8[}}<D~^<9P/5?z?3)~¿w2{ _Cp_C+ 8įY!~!ya2iQl6Fial6͆iQl6͆iQl6͆iQl6͆iQl_ϋ7'+AA=$œ B7OyHYA?YA@YAAYAB/)!:ZPjB-Cu!:ZPjBCe!:ZPjBy*T}*Tρs`<As`:As`8Ays`6o8 @ǩN;y=8|q `Cf=$'ƒGБ!ttBG!tdBGБ!ttBGБ!ttBGБ!ttBGQ!tNգwS蝪GT=r;UܩzNգwS蝪GT=z;UީzNգwS蝪GT=z;U_84+f?3gV̞3+yfϬܙ;Rgfʜ3+qfά733b͖ul9[f֡el;[Ζul=[gϖul?[ϖumA[gЖ!dB|5^3z~5_||5̗_~|5`߀|6̗`߂| 633gg@Ϟ9={z 33gg@Ϝ==sz 33gg@Ϝ==sz3fϘ?c3fϘ?c3fϘ?c3fϘ?c3fϘ?c3fϘ/!ta]BЅ!tq]B!ta]B!ta]B!ta]B!tQ-(Y? gQ,̞ѳ(yϢY;Rga,ʜ(q΢Y7 F]>w-~Zܵks]>w-~Zܵks箅]>w-~Zܵksrzz]@QVVQ,=2,P(* = DTVVQ(jBVQ(qkBVQ(1lB N2m6Nm6N>m6N>m6N)qB)yF)qJ)yN)qR)yV)qZ)y^)qb,kAg%Йtf?gYsf=g%ϙsV;gΙsV9g%ΙsVޜ7gEX/z֋7^ o`pX/z֋7^ n`xX/z֋7^ n`ZFU te]?WϕsU\=WFUse\;WUse\9WFUse\7WUisLƴ1mnJ榴1mnLƴ)mnLƴ)mnLƴ)mnLO˝9YDY5@Ȳj"B&EYD8Z5@U DhZ5@U DZ5@U @Jh&6p:h٠ '6 p*hÙ&6hhY&6h h&6hgٟ&6-vT ʉS9r*fNЩ:crT S1z*gO>rT T1*gPhCcu!1:PBCcu!9:PBCcu!9:POB=Cw!;zPB=Cw !3zPB=Cw !3lCHWE =)uV\ Ί_A ΊYBbg,!bY1KB쬘%hظdž<6nq? zlcÖظdž]=6na_{lcظdž=6na|lc !uX cUx;|]!v;|d A> YBb,!GjY@5fԊS+|jfO評<5VԌZS3tjeNȩ85VԌiNtğɢ ~#"}O Gt,TTWߓ' #}O GL,=Y0{`8؞; 3nx3=l(; k; sjψ; 3i8; ;BChp !48BCh` !48BCh`0b^ t !*JuxtxtxtxtxtxtYj@ a4(|fOi< Q4F0teNi8 Q4i?rgNvVM(=,"H Ъ<cQVM$AiDVM$jDVM$jD ,^߰S7꓿ao$߰CF 7쏿Q{ o߰3F7싿Q[M;b8#gbL39v&<grL 31~&@#hrM !i|n|o|o|o|n|n|n|n~|nn|n^|nys`8G΁sp:s`<Fs`>s`@Ft`b_*E^o(*@u˪^L@Nj\:P)Eҁh/ w;؞`{7gK=a{6w[؞}a{7wk؞a{7w{؞a{7!w؞=b2 .ˢ*&K" ˡڋK ˠ ڋ2mg.?r\|㖟g->jIZ~cg,>b Xӟ˝9YDY5@Ȳj"B&EYD8Z5@U DhZ5@U DZ5@U DgѝFvFmtgFwFmtgѝFvFmdgѝFvFmdgѝFvFmdgѝFvFDIDHDGrDFbDERDDBDC2DB"DAD@D?Cixnxoxo~xonxn^xnNxn>xn.xnxnxn}K/gt}K/gt}K/gt}K/gt}K/gt}K/e >o 7|>xn C7x憏>pm qw\'8q;.Nqq򎋓w\'8yʼn;.Nqq⎋w\'8yʼn;.&?Ll1bb&] &ALl 1br/f!&CLl1%brOĦW[r렯^}J諗B_Wr9롯\}+D_&EWrY뢯\}論Fo4Vb[>m$Ճؖa[=mCV`[>mV_[>~mV^[>zmV]ߝzv"* Ce<(QC2@J@2Ce<(AxP*A ?Tƃ<^;0PjBeqD WM1зj"w&j"k&Pj"_&Hwqac?+~. rݏe?R=8 ˂+4. ,G ˂+. N|hbVZ1P+hb(VŠW9*\`fbhUT9* /vȁUBOJ B_Agd Ad Ad A'd Agd Ad AڎBx*AIi !n?^%B܁%!Aw!g A ĝYB^,!%ZJ JJJJ J J J J J J Z2j%P?f S3zj%O੕;5cV ZS3rj%N75flhf8q6 3g0u6sg8y6g8}6g863hiCg!3zPBCg!;zPBCg!;zP+kAe̟V2}ZOi=eʝ*uZNi9eʛq2msW]-Z?w|jsW箖]Z>w~jsW箖]Z>wr_RUBOJWV!*Eb,!J%KB쿒%!_d A0Bj*AI !\%Bl%!af A0 vYBb;,!bN^ t !Juxwxwx wxwxwxwlC[p[omն[j[nmuvٖ[d[omז[^[osmv֖[X[o[mUԖ[R[6y_y"\ S'W.}ޏ#C:: C::#C:2#C:2#C:2#CHZR[oIm%[R[nIm%[R[oIm%Ԗ[R[oIm%Ԗ[R[oIm%Ԗ[R[oIm%bЕ!tu]BW!te]BWЕ!tu]BWЕ!tu]BWЕ!tu]BWU!4li1C!eE бf=$'ƒw~5ks]f>w~5ks箙]f>w~5ks"kAha-̟E0}Y=ga,ʝ(u΢Y9ga,ʛq0m^.wdagՄ#"Ϫ" c0ehDVM$iDVM$ajDVM$!K̤7^Lzq3ͤ6^Lza3ͤ7^Lzq3ͤ7^Lzq3ͤ7^Lzq3ͤ7^LZfTS}n*M<4gTSy^Ki>,gTRyNI)IBɳBBBC3CSCsCɓCɳCCCD3DSDsDDɳDDDE3ESEgY:+ 393} 39+y r9+u 29+q ̸9+m ȍmM- ǭǍmMzJhC~BhhhhhhhC7:lUЃ!`ƒux*E!`YB!`YB!`YB!`YB!`YB!`YBbx@zR*<AV.ze A ^YB⎯,!qW+KB/vigUГ\Ϫ !N]!d As>YBO8%!d As?Y"߱fJT3jO>5fJS+wjNԩ:2fJS+ojMʹy_q3';&YVM$QhD(G&j"M&j"S&`j" Y"CVքS{ YY dʚc@d=D2deM$!+k" YY dʚH CVDzz YY dO!2deM$!+k"&Ȑ5@2deM$!+k" YY D=G;U0=2wX0Bw1}ȾSł̾SłоSłԾSłؾSłܾSłzZ`jw !3zWZgX!;zS`Jg!dB#Cht!4:FB#Cht !42FB#Cht !4*lk/lˆk/lk/lk/lk/l3: u|'NP;wB: ux'NP;wB: ux'NbC"ʊ@yR@(+<@(+PVxPVxPVxPVxPVxPVx,l03{n0 6l0V{l0vl0#̖{l ֕ZWlk]uZWnk]u֕ZWlk]uŶ֕ZWlk]uŶ֕ZWlk]uŶ֕ZWlk]% % % % % % % % % % % % % % % % % % % % % % % ;BChp !48BCh` !48BChPunו]Wv]un]Wv]unו]Wv]unו]Wv]unו]Wv]unו]WW't_̼DU"CV\y*%h,\yP|<^(K?W0%+AЕ e@&kA7%Ѝtc?7Msc=7%ύsS;7΍sS97%΍sS77M kuy(6Y!=@,@,-y(6Y!6Y!6Y!6Y!6Y!6Y!6;3/fpUȃi B]fguY"qa~v\g%f huY"j!Ԩ'ը ݮW.PnY"j:KB ]g@a,5v%ݮD tbAK|VE < ":p*%:pO\%d@%>Y":pO\%!pr# r wb b wr qvqqvqqvq?'.wdagՄ#"Ϫ" c0ehDVM$iDVM$ajDVM$!w\gXwTwPgLwHgDw@wb pU`X0GU,*r[VM(=,"H ڪ<YVM$alDVM$kDVM$akD^&ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&Ȑ?"CVքS{ YY dʚc@d=D2deM$!+k" YY dʚH CVD_<Ȑ5@"CVD&Y!+k" YY dʚH CVD&Ȑ5@&ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&Ŏ+5_ t|UxB]'6g AMdzĦYB0b,!x&Y@hbL3)|&f虔<gRLI31t&eș8gRLIiZ͢Y6 fQ,LEi0m͢Y6fa,Ji(m͢Y6fa,Lߋ ÷˝9YDY5@Ȳj"B&EYD8Z5@U DhZ5@U DZ5@U dz<Ȑ5@"CVD&Y!+k" YY dʚH CVD&Ȑ5@opyYMYDMYDMYDMYMYMYMYMYMYMYeWU`*r0|8 _"WU`*p0|9  _EWU`*p0|9  ]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W<Ȑ5@"CVD&Y!+k" YY dʚH CVD&Ȑ5@,"vyY\EQ O?j"Y^EQyu,"_E,"_E,"_E,"_E,"_E,"_E?&ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&Ȑϸ<Ȑ5@"CVD&Y!+k" YY dʚH CVD&Ȑ5@?<Ȑ5@"CVD&Y!+k" YY dʚH CVD&Ȑ5@? &ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&ȐV=]@[v =?j" Yo( Yos޺*2duwUdzȐ=W!뭻{"Dk\A^G:d uY#zA^G:d uYzA^G:d ˪FFFFFFFFFFFF&ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&Ȑ&ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&ȐrY"w\ֻH.]$.ze ErY"w\ֻH.] .zerY"w\ֻH.] .zǹ,?yY>'xyJ>'Ÿ^Μ,"쬚P zdYDY5@DUy Ƣ,"H Ҫ"4H Ԫ"LH ժd%Y˰JJĘJ̨JLhJ̥PJJ8Jʢ JJJʟIJIʜILOHOHOHOHOHOHOHOHOHOHOHO   º$º,ú4ú<ĺDĺLźTź\ݳU=@, OpO<@,,@,,@,,D?AΜ,"쬚P zdYDY5@DUy Ƣ,"H Ҫ"4H Ԫ"LH ժ"dbȰUjdL5:FpM#it, ё42F0E#ht !6/viѪ'%Gƒh"qQ(KB\|%!.? e AKD h1vg1{vg)wvRg)svg)ovҦLNi1m:M6ҦctLNi1m:MǴ6ӦStLNi1m:MǴ6O_eΜ,"쬚P zdYDY5@DUy Ƣ,"H Ҫ"4H Ԫ"LH ժ"dq3zGUSCgD3zGS`K=Cw$ q3zGQ C=C<B*AIiǪ !X%B%!n7}d A> čYB֏,e-#h3̟g+|̞=g3xʝ=cg3tʜ=#g3pʛ=f7mgc-;iƎ5vڮn7kWcǭ;qN4vܦ.7iGc-;qƎ3&['/?pÉ'/?p'.?p'.?p'.?pY2f%?gf 33zf%;3cgV Y33rf%73ff<>,wdagՄ#"Ϫ" c0ehDVM$iDVM$ajDVM$!;M(=W:>;M$!NyuW<>ȐG~^ȏ"C+Qdz#? Yx|G" . MYo"zA֛D&7d MYo"z@֛&7d MYo"z@֛8o5|ԄSC>j" Y_{GQvUdCV!kwYE=dvUdCV!kŎa58R#jp@ 48XJ#i` a48BR4{EKW-^4sK3W.^4{KW/\4{LW0\4{ ELW1\4{̅L3 PG($J@) ^N'@Ej PUe/,TxiBB Chq-!8ZB Chq- !0ZB Chq- !(섒tU]@WUse\>WfϕsU\@9 r !gUBOJYƒJu%!>ne A\YB#WЕ%!>ve@h#;6Nh; 6Nh;6Nh;6Nh;6Nh;6d-#RU S)~*O=RT JS1v*NЩ9#RT JyS1n*M ̛Z;.VE A j|@P5< A j|@PFb9?a8x;Ca8p7rgNvVM(=,"H Ъ<cQVM$AiDVM$jDVM$jD 3UVȳzHSYAQYoƒƒƒƒƒƒh-#HG 5XVlxb`ņ+6X; hUГR+UABlJu (KBl%![e A V@YB:bЉ!trB'!tbB'Љ!trB'Љ!trB'Љ!trB'I!4oh1C!eE ٓ=IɓÖy[UBJ<ƒux˳Juxx˓%d Wϕse\8~q 0!YbfyVE =) B,*E,O8˓%!d Amzx{e-f3mLfϴ+mL=f3mJ=f3mJ=f3mJ=f9ZAis`6́isP6Ais`6Ais`6Ais`6AiZM6=ӦWL^i3mzM6ҦgJi+mzM6ҦgJ6.&...-------@cp 47@c` 46@cp 47@cp 47@cp 47@cp 4h-#hT 3*~FϨ=#gT Q32vF9##gT Qy32nFxmxep]pUxMxEx=x5x-x%xLO?q3';&YVM$QhD(G&j"M&j"S&`j"Yr>߰O~>߲O~>=ߴO|>]ė߶O|>}߸O|>ėBB Chq-!8ZB Chq- !0ZB Chq- !(5-f=$'ƒbay,ff10هY1f)RhRkf/Zhrf/bh撡kf.jh没f.rhҡkf.zh9bPb%Pr%Pr%Pr%Pb%Pb%Pb%Pb%Pb%Pb%PRUE5<%<߿߾߽ջ߼Ż߻ߺ>?,ߗ9~$&X[īkK~-y\ƶ s?֖&X[cmI`叵% ?֖&X[XP+K?[O~s+TJ?SO|$'4ɇI?KQO|s$#X%6Jln(QbG[%8Jlr(QrFGɝ[%:Jlv(QrĆGDB'!trB'Љ!trB'!tbB'!tbB'!tbB'Y2J39+~L93zJ393vJ3C93rJ3笼93nJe\6ͅisQ\6ͅisa\6Eisa\6Eisa\6Eisa\6WY˴*mL+檴2mJ+ʴ*mLʴ*mLʴ*mLʴ"mz9^'§a4F0xNi: CQ4F0pMøi6s}=Sk#}'ss|=s{]=s{=ڣscz=e@<1=u tc{@:1нy tc{@:1нݵgs^]{vٵWcמ}]{uյgS^=]{tѵgC^\{s͵g3^\{rɵg#^}\ 7h*AA Dv_%|$.I^铸'yOb>}$/I\'qOU?~$/I\`z}CH秮@zR:BuUxOQ]%B!Y gUx]!6[d A~ v?YBbß,!O|!C" BU⯋ CYB,!qP|(KB\>%!.Ё; C:0C:8 C:8 C:8 C:8 ![UBOJkW!q *E0FB#Cht !4:)d r0v 7 dqa|@6 dq@Gqtx GȑArt(Grd0GAsd8Gsd@G0;yUГUAB\üJu9KB\Ü%!a0g Ak 5Y"v!"DC" " D D D D D D  4bBChf!4;fB3Chv !43fB3Chv 3jN$מL=\sRr̵'kN4מl9\{!eeeeH*s*{2){2){(s(s}'su'sm&se&s]%sUrΪuV] YuUx;]vVB;f UΪYBbg,!jZYj@-e*|ZfOi<-UV2tZeNi8-U޴Vi#}Xކ]Xބ=X܂X܀W~W|ŽWzŝWx}Wv]Wt=WrWݖ;o g2s-Sg:w g:{ g: h: ![BCht !42FBChd!4:FBChd!$Ν;w\ys;w^qsΝ;w\ysΝ;w\ysΝ;w\ysΝ;w\,nn~l}v͕3u?JrJJ'eIQIvr5E8 n4OJr 4\rgѯk{_x^pꅇ^pG^p^pޅ]pڅ]pօG]pŋl m֋n֋o׋p q# r3 sC tS uc vs wͲ4+f?3gV̞3+yfϬܙ;Rgfʜ3+qfά7҆2m&0m&(m&(m&0m&0m&0m&0m&0m&0m&0mF^׃~]u=z_׃~]u=z_׃~]u=z &.6>3;t&{St/3ٛ‹x&{"L*!L*!L*!L*!L*!L* AL a8{=LsDZq0s9Lq㴑_ףG]u=z_ףG~]u=z_ףG~]u=9999999999999999999999YS=;S=9ՃS97cS95CS=3#S=1S=/R=-R=+R=)R='cRevw;ou|6w9=oqxw7;s4]Uiz 3p4=JMS4=PMS04=VMSHZծS;3jW@L]Ѵ3vҮDGhgJ+vϮ;2ggJ, ^;2xe䕹+{W>^;2ze약+sW~ _;2|e+sW          Wә靈_Ng|=~:uWԙ勉_Rg~M:EuWՙ荒_Vg~]qqqqqqqqqqqqqqqqqqqqqqq#%')+-/13579;=?ACEGIKMO萵C t0ϡ9>`Cs0x9:CPCs0p96\̩"NtOVEğ)Dj`,"Q"(5MO!BS"@5MO!T"X5MO!B-v\UpTU`LT`DTp<Sp4Sp,Rp$RpQpQp Н; C3;C; C; C; C; kA Px H Op O[TTTTTT @pb5EGP^AM⿋AUB*!ppP~88J@T% F-vijtSt/MEB$B]%!NFW AS ĩUB*!ѿ!2VZBChe!2VZB+Chu !2VZBBQ2@ Gϣyd<2z< G#cQyT<2r8 G#i;s4]Uiz 3p4=JMS4=PMS04=VMSH$kVOJ'3ItzR4=1JOJ'ѓAzb=)>OJ'Γ2牁Ĵ.o")Q諄]UB.*!p}JAE_%3 :`db]W ]AVMf+f+f+f +f@+f`+f?oo;VUF&+e mMUD]VUF&*eLkRF&*ezu׍O_7?~uO`7> vCOa7>vOb7>,vOc7>%YJJJ̟ILI̞IJIʝIJIʜIJIʛĸIJM#N<>|O:>lO:>\cO:>L#O<оH|v勷n/._]x|v o/._]p|v o/.ZFС:@PLCs0{Fϡ9} 3} 3vE~+CWw\_>rE~+W[_>nE~ۊe+VmQF#Fn6m4jhmQF#Fm6m4rhԶmQF#Fm6m4ni)m6ƴٔ6fSlLi)m6ͦ٘6fclJi)m6ͦ٘6-vӦ(BJ6!&EUBUBUBUBUBU"BChg!3vBChg!;vBChg!;v8;9EKs)AM⿋ *!sJB; *!sJB+lV Zy&h=[Uw@+oVYygϪ[w>n|VYy׳g=Ϫ[pNq:逎S;qtO}?qu y[9MQi /B9M⿋ 3r 3r 3r 3r 3r 3r 4bBChf!4;fB3Chv !43fB3Chv !5-v):ЗRosSxۛAUBbs\%!9W AUBbsZBChu !2VZBChe!:VZBChe!iC(3C(;2C(;2C(;C(3C(3C(3C(3C(3C(3C(+0-f jj"55 lj] AU"T% AU"T% AU"T%+D-f]5J):ȗjXSxЕĚ@QbU"rX%V%+GU@QbU"rXlCH})Mvn /Bd&Ed*!qs8ٹJB\%!Nv';WpE6Bm|{ 7@~x{·7>mzx{@r3{ N 6m@r{ 7?q{Ο۟~˝9UDiBɪ4=BMEUD8jB)DhjB)DjB)$ȊqN(P;wB" E~'N(P;wB E~'N(P;wB4&o`M5yk X7&o`M5qk X7&o`M5qk I|B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}Brj+FjjʩѫFrj+Frj+Frj+Frj.k6fgL]i3mvδٙ6fgJi+mvͮٙ6fgJi3mϸܙSE*D?MS(4=XTE)DPjB)DjB)DjB?.@ֻȐU5]쫽 YUSȐU5=.2dUMO!CV2dUMO!CV2dUMO!CVdᶟ,Pl}>:$|}V}V}'쳪쳪@2En)rD8Oq"7NiȍDn%r$4G1"7MqM3Dn!r4?!7MIϴXyS%(J9ME:MyNx‹z‹|‹~‹‹‹;9&sL<1qc0a9&sL81yc0a9&sL81yc0hC(1C(9C(9C(9C(1C(1C(1C(1C(1C(1C() Z?q~`Ĺc'O8R?q~@yI' O4J?q~ 9Ic' Oic&mM)6qOlؤb&nM 6i/lVؤa&nM+g~g|gzgx}gv]gt=grgpfnflƽfjƝf)Bb7EGPs^=M⿋=UB*!p}~J@s_% DЉ|J'NK:uD؉|j'N˝ȷ;ѯw"D|'OKȷ<`(Ll(Lj(Ll(Ll(Lj(Ll(Lj(Ll(Ll(Lj(Ll(Lj(Ll(Lj(Ll(Lj(Ll(Lj(Ll(Lj(Ll(Ll(ܙSE*D?MS(4=XTE)DPjB)DjB)DjB,߂3333333333e%PaOQOQfOa%OaNaNaeNa%NaMacm4cm,Əbm$ئbmkئamKئam =O' zr=1!te]BWЕ!tu]BW!te]BW!te]BW!te]BW!tex!EK"!$B<)JB<+JB<-JB} 3g8p`>Á} 3g8p` >} 3g8p`>} 3g8pp>} 3g8pQ 42FϨ>gdQ32xF:#CgT挌Q32pFȸ63z>3r?#3z9?3zY?3zy?3zș?-vgt->[|Fn3gt->#[|Fl3gt->#[|!te]BWЕ!tu]BW!te]BW!te]BW!te]BW!teI'pxxgxԏ'pЏp̏pȏgpď'ppp$-{ '7N*V8pbcɭ{ '7N.^8pr-{ '7N2f8p֊[+&nb֊[+&ob֊[+&nb֊[+&nb֊[+&nb֊[+&n$Wc\s5&՘/ f EyQ0v^:/ e #EyQ޼0n^6pnljh}f]d=b`^\Z1Μ*"4MW!dUDiBD Ƣ*"5MO!R"45MO!T"L5MO!U"d5zvk٭ѳ[gFn=5zvk٭3[gFl=5zvk٭3[gFlToH~ToGnTnF^TnENToD>ToC.ToBToATo@So?So>Ez|#GOA=>r}1s#GOE=>r}!~']O˞ȷ=ѯ{D~'OK>}D~'O˟eݔ@7ЍsSܘ>7ύscܔ<7Mscܔ:7Mscܔ87Myscܘ6ܙSE*D?MS(4=XTE)DPjB)DjB)DjB$kVIISPMJIGIYDI)AI>Iɓ;I8i?.p||>T}>T}ňJb|.#H}>T}\bU_1هaCWx5=oUetkRD] lM-kUeTkRƴ&*eDkRƳ&*e4kRƲ&*${yO>}hm_)_C˴$BY&'SC,$BY'2#?IPyIO/e~~l'}+tt"CtCOK@'?_PtC W@'?_޽du/{ ^w⷗dl/Z; N~bdc/;I >}_pwMU>\嚦iz>kMS4=eMS 4=_MS4=Yq{mYǎ7c0߂u:6^nNc:6TFc&2uf0ڹӮj^]v~;7u8rs]mw;7u8z'DZqaQA1!̞|Ƴg6=왍gl>{f3Ϟ|Ƴg6=gl<{f3Ϟ|Ƴg6=gl<{f] 3vϮٙ>gg쌞]ɳ3xvٕ:;CgW쌜]3pvθٕ6Y2m&3m2&+m2&+m2&3m&3m&3m&3m&3m&3m&3m2FRgN5Ξj=8sqT̩SgN5Ξj98{qTƙSgN5Ξj98{qT,o3_+gW~f9ro3_/g_|9s;̗o3_3gg|ќ9Us?!zu:^BWЫC!zu2^B W+C!zu2^B eݕ@wНsWܙ>wϝsgܕES;ES8Eis\h1]""%œ"Bg` J3(wΠ:2g` J3(oYhfa,Nő0sY;sga,Nѳ8{Y?ga-N8\5-vUsSt/WMEB|$B|\%!j_5W A WUB*!Ab&n4EGR^ ĹM⿋ UB*!qF8JBQ%!(kAhd̟Q32}F=gdʝ3*uFΨ9gdʛq3*misU\6WUise\6W͕isU\6W͕isU\6W͕isU\6W͕isU㺿Ӻ::󓺿󃺿scSC3$GR&?MM~<5jGT&?UM|\5jGV&?]M||5k`!h"4])A6A36 )UB✍*!qF8kJBQ%!Z9iuנ՝sVwY5eu]3VwX9au׀՝UwW9]up՝UwV9Yu`՝sUwU9UuPeΛ3;qfG̙:3SgvΝ3;yfF>gfΟ4;fF B Ga8}>Gq0u:Gǁüq8m}_gJ-}_g~N=}_gRM}G_gV]}_gZm}_gy^}}_gb}G_gf}_g~j}_g~n}_g~r}GEvB -Z|'Nh;wB -Zx'Nh;wB -Zx'Nhe%P`OPOPfO`%O`N`N`eN`%N`M`MLLȴJȴJȴLLLLLLLȴWstWsdWstWstWsdWstWsdWstWstWsdWstWsdWstWsdWstWsdWstWsdWstWsdWstWsdW*Wͫ_5|ռUW+_5~ռUWͫ_5|ռUWͫ_5|ռUWͫ_5|ռUWͫ_5|,aƁaqaaaQaAa1a!aaa``elJi1m6ƴٔ6fclJi)m6ͦ٘6fclJi)m6ƴX6?`m|kX6>`m~kX6>`m~%qaQA1!~᧞}ѧ|YPdCyE9PdC}E9PdCyE9PdCyE9PdCyE9PdCyEChe!2VZBChe!:VZBChe!:V22222²2ò2IJ2Ų2Ʋ2q;gh=D)wѸ{Ɲ4q(;gi=LiwӸs5qH;gj=TΩwոsF* )) )) )) )) )9d-PLis(m͡96PLCis0m96PLCis0m$ p2' x2' p2' p2' p2' h2|~G *5EGRUj /j  ^ ^ ^ ^ ^ ^D ڳgng͞=s/{V̝ٳgncŞ=k{سgn_ڽy=kzh恁yp<0s:L΃sy`<8{>Nσz`=8B0+D2O<+DN<+D2O:+D2O:+D2O:+D˷'\̩"NtOVEğ)Dj`,"Q"(5MO!BS"@5MO!T"X5MO!AHWUUto.LUDU OSUxSUxRUxRUxQUxQUxPx w@*:ȗz‹w@*@*@*@*@*@*@b3f9s90kaì1c3f9s50sa֘1Yc3f9s50sa֘Ü_q3;MU>YQiz>GMS4=MMS4=SMS`4=YeXJ1u2Nt:M'sd(Jqt*Nѩ:AdJs*sNɴd`8w>bˈEbDU1>}>T}WbU_1B؇CW+fi%gW,5/jAE ?(P5H/jA_Ԡ(oUl"Ū`^U @)@)@)@)@)@)@hA 40Ϡ>g` A30x:CgP A30p6 w|^:s|ûǷ8 o|{û&ZMTDMdDMdDMdDMTDMTDMTDMTDMTDMTDMd@b&7EGP^IM⿋IUB$*!p~8J@_% NPq%EJ *)UR8hPIᜒ1%ESJ ()QR4p@I|$EI &)MR4<@4b獆4EGPO^%M⿋)%UBᜒ*!pRI~8J@VR% + z[CMQj /BCM⿋ Ç Ç Ç Ç Ç Ç oB{EKimSxw6.7V Ap čUBV*!q3nJB-vm !96BChsm !16BChsm !16Цl1Ch>("_j޶"ٚ@h* ܻV%{֪D 4pZQiU"\OaOQOaOaOQOaOQOaOaOQOaOQOaOQOaOQOaOQOaOQOaOQϫ@ +@ @ @ @ +@ +@ +@ +@ +@ +@ +@^e-#2̟*|̞+2xʝ+cԹ2tʜ+#Ĺ2pʛ+抴9XyS%(J9ME:MyNx‹z‹|‹~‹‹‹ZBChu !2VZBChe!:VZBCheU!-~pK-퇶~hK-~pK-~pK-~pK-~ȱO]|3.>oqO[|-?g1OY|33oVj"Vj /"YIw xVA]U"YJ?+wUh*U%ZOni?[Omi?Ԗ[Oni?S[Oni?S[Oni?S[Oni?;OS>N;OS>N;OS>N;OS>N;OS>N;OS>P!¦("_Q MEBl(l]G6V@ U"zdCaPX%G6V@ U"FNn:FNo:FNn:FNn:FNn:FNn:|TWɮ]ͧOv5j>|TWɮS]'Ou5j>|TWɮS]'Ou5ow'ʁ W ?j q?j v~K !BY/_(K e ~,/%B., ]A_z`E.p h=$BXC.L L=DB3+.@ֻȐU5]쫽 YUSȐU5=.2dUMO!CV2dUMO!CV2dUMO!CV2dioUOJgaIƿFL40~v'4X~v4l~v4~v4~vTvs][غփ.uA_]VzXՅBuK]HQA3%("]" /" %"""""",kAY @?>ٓ=Yɓ9Y./ϿG-I7 26e351e1a38eb1524574e86ab130ea4db780aa1a4a8bb741d37595ed203f931c-prestodelta.xml.gz000066400000000000000000000005201372027637700425610ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/02/repodatalPn E m]]W+~8}}}!nTV{aFq(SJp0mcE"INM3e-PQM`pD2@=B訣\Γќ*ꙛӠ1c|]7 J`7R4jd$`l}jS?j)^Ps;|o?d %,egK.Xar̍HU6|Șga{˛ds 3 TroDZK3`mej欁)L:*W%l~_>32d3307b672abf7356061912fa3dc9b54071c03a75c671111c1c8daf5ed1eb7e-deltainfo.xml.gz000066400000000000000000000004541372027637700422040ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/02/repodataPmn ){uӤMA9ު6zҦ;onvM_ Ѻ}rh#Wb }9$`eR0Z^UAC%_0FiaXWeFe⧱3vA/"%Djgq2/(Rq}][1 5ז˰R*/8yKR0mSK!lS)R`Oe}e~ 5a8e6bbb940b151103b3970a26e32b8965da9e90a798b1b80ee4325308149d8d-primary.xml.gz000066400000000000000000000016741372027637700415410ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/02/repodataW]6}_aJ !CȈL_VJS1lji_k C*O89ؐém+h#q$W],P-XV2¥#),#P\~]{t0~`'vCw䒵PTs6ڜni^"ҜC77FSUhh\Ӏ^?ҚRx.i [dAooM |ʘmxݖ/YɶQpvDgQU,NjK!#>[ 3RhV3iYTJF>:ȥ ͅ(p-:afy.a@TE[8ŬCi,k(P C<JZXfՁfM53w6Bp mq=߹K`&u]헲[&*>΃tJt |8> OukOOԇhIp|m}8>醗2?fY8S 5b37f89f9f4474801ec5f23dc30d3d6cf9cf663cb75a6656aaa864a041836ffe-filelists.sqlite.bz2000066400000000000000000000547761372027637700432570ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/02/repodataBZh51AY&SY:H _ϦOlO׶w=_O=Wp]wѸ=6}  YJUAB9(2mI Fۛ̕mE+8TvuEr H-5gI(( `h44iF& d ѦL4L dLA&{?4$P!FOU*ekYO~=>?7~z(D:DFMٯ>ű 4`&P@|T@sZ-w fm}OD5ΕyhЪC&d>r4:7Ċ4V>\ wM-0˚w/v <^C!2 U*ЧCF]ԡ.CN,uҢ,GLxJz jۡ誕]`OkGVcnPs-r^N!)it? Q "I"$R"xqV %CVHԅ`FHW QD]k9۾s7HEi(#B6~G~y}9]K 7X-%r.CPxyl#H"qd+L -%&}ʻ6N  XyOHt?~Nyh3"-X >W&A"$15P8ɮ6 PnY[Z 7u/nJM1/,7sH _r>߲Ӹj瘊lϸ=6/#7wYL:K'Xby܂4Ɂ @Q&t&} PSsAd ۙ3fmAXsq_'o?) ;PYL)db&(֠?qs˯/$zd+ܗg9ulZ\y YFw_Ď# b#xF.|ɦX10g'ι'Iޱk9ϊj4PiɩG@&h-.-4~=@2z-oqvaojM9.I;[Phdʙ\n1-7#EklG']rMҒ k$ya-_6 B7c!&d-9 oB-CÀ֡0ԩ=LQٳ$r j~+ toY.$&tgE(_pL27^'r>ҋV[y)@OI*i&XsVݵ}Jְ4/:s].t)xRB6 $ٵ1B=,)We`s V QCRZ10{ݔw1"/`c? /XB9{2If4~W֜cЃ=]a})} >4ӎOcA6I {TlB%$M ibc9GXA>(Z 7lt~;E;(c| 96Ά#V1zH5vC`d!}͜d"G1+Ή,R,)[P11{f3X'چL gKQ bqxHkJԴ$/X1@F2r{j^"Ȥ4ccLnB‘K`N֬ :jn'm.aqBa^?t+Lc#xFK q4x#_/G];4^Hr"g`ClX{?,IX9@,5CV"^L,pm/N*m,@%)!i2Kr  c{(,Y {ppnQ*/EXc1ܲ=WT 0"2ErzъЄC{|h9~*oW4(^FdTZ /Rn%^k?Zw"}]`.yA14_Ā1+!;b5%MPң]b$٪K$ϻG0Bv̛tBtst9E줆ӦIVUR(JRpj5-8r34$[,ܛgxuuihNֻn$GA3߮'N3Aq.eIFFCowi5RHĹݘ!O'^9G ĚW\pwuvݭTmkkFNm4mÒsepp$'o[/OϠy#uj "k{Y0}6,s$'loy{LncNY7dH$XΑ=h>,L|zxh ]f( ս6jZrjt ŏtE.fxգ>9;/<'|yR<;^^Z@@MN% ʃhaB9}^DPؐ/33'0!EY>~_s , $A(̶mYYyV_ۜ|UgQ( "|/͗'|t{Jqt7.*H݁"Pi!$h9FFP+'w~6KQjVm]a|e|$?.C T=.#!/:]؄:y$,r 8|B10t`鷗85t/>"N LX@ pR-Zmfc#ɑF e2i#" >hn$M%2HCZ!' h.Ώs|6jEί I‚s:>/[[l9SaLK :&>d>@'*t_AN;$HD 5W-~&ck|19lmA0 @:w-|P?hmc^yhv8E=FAk)rIo4y| F/O^fU4]Ig_/ 3PX=8gJ,m,='WUm䎑6vH @Kr9>~` 7%I?)3>WfyTBqY=,('6cɝ}>H@-o})]=6xEaejE=G\$˒Iu|ڜ;ETsGVBq~#(hk4zw cNCc#)Q W"RA"l9P@ k/=wZSdf6xBuT%*^qqHYV- 6=T|^bxyTLtgU.Wrc_'K^ssjS? k}gSTogs {usyTTӡdž5R' L]/9[^ aϣ3_TL{k<* W$ 7._<KٗL}I7dI@m(gjm݌Ukj=g o H(/_lj!e NU cv$|ԩP?|1UTx/h;:^sxKE& %|CGuɝ;JsNW0*MziDJ."nQ@kR W~ѱdĉ4{ǖ$AFHNݶݯc-]T뽸$4]Md)(ob,ʦO /A2!EP "KnO'ۓomomoɷxŽ}*̹r{3|in2?γ$븻6d:;/G'IuUmU[>/jIdKE>w;+ƸtQ5R&UazV $ʅzN*>|hiVu?'Mn8Mg.Ng3C=G{aSKY508mFn"+SS[\Y+\_$Tmz=^zG Kn{7w=>Ww:nW^;|TyU_8}7qwɫ=7J*++/UUUUUUUUU]ZUUUUUUUmUUUUUwvUUUUUUUUU{*U{ܥUꪪ|z%U*lǯ{}{W*wt""""""""";xq QEQEQEQEQEQEQKQEQEQEQEQEQEQE.DDDDDDD`DDDDDDDDDDDDDDDDx""""""""""""".R(((((((wb"""""""""""""""""""""""""""""""""""""""#"(((((((؈"""""""""""""""""#ʈEQEQEQEQE.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDs%QESޥ؈DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDnnpڀMMl-Y6IG5_3،6!e}tk6]ξ7OtKS׳V41Nt|ǚV]M^V$SUYVakEC6$KS3i ;7m+SC/ͶJT0+qlT],} 7uh2Ihw"܍Z?5_,i0K\ܢQz%HEL =.F.rջƽit<-C̜T|Du*N:>󩂉?ō7^8K*XLvPyk3!tB*~M+=mqFVVgދC|nKEm=b*Ǔ{fͻ0$L "jG?y:9aQἌR( 0"b B2,A$"H( ]=i׾4MQ9Ft3m~L ըj[n%7w)jAB "A#6Kf}Ғ!ȓ%d.s8s)s(E>~Թf0۫n5 ?R H;}.-kak?01Zp?c:Rt""unV2H")9CN_ "Նʾil#)K=#٨=HpTg<KJEi`ARAӑbՎ^?nw!E":ruoQJƒCHW }_,%!fm{˳c4+ YOA_Ɩp-@fp-n]\rc6]gK& y}5K/YN,p# H}~?7ӻt~F=T?ysznӞW47]BRβJ~='OQ;}F|w#e[[e~QOi:nе⧖k%DO[kj뭰|fbP LU^u7¨R=Ymw|V-=:9s-/-s1~{i_].=DdCt>ʺ }O_-K"a<|zqi~#Oivs܋{u͛-#2 ?t;2=LmgyRcӹZQw=OZ\f?[G{z]7Z GqYfnSߢxnQosjly[N5+NAIBT}Xf0qzIH`n3iVѺO+yE0jNZX@Ey_ /^R {݊| /c7Ot[)`c͐z}M|wwcU~7QSڬsc˹KߓKoVqAk]~ŧWewӰ|2ki]]Βcj2ھD~ "mO9Z5yVL2t &?ED1vP= ܰl몹2J\,'t['Z_?.f28]KCv|g7I\} _i([p|fk,_mewlMW.ML][{.{Q{f/k$yQ5[IqqTT/Oqg!رt%[ {X4=0_Onu  yLT';=kEk1M2\̰.A`.@^@r|n5_'÷ym*[M7_`?_ik{tȿ%/kFFMQb{ 5Z boOɤr.4g}g^{mf#(_Qps۹ǪS>>aKPTc|+,1__p[}PWhc)ߧgz1M#ҖX{?ƚ60=ܨo^swsuuBq:w5ư 001w݆7NS ] [Z+4S7Gʱt?ێBn.Elqu]rʃmmhչiÍBf(Q-&"ۙ_kJpRNNpnM;u8- rp-vqiG-MoIXf HRCkoA FZ̿ٷj3@,AJ[7noiFlYwsJoN|uVF KE-+H|o]rN'\'Eg޷Ux-{5҉ ''~u.ݎwfD<6n4W9fS-ň 7:QӒzGуt$q5V(Iar. D| 5)bVj)Yu*Gғcܶθ}oͺUOL|6Z{Oom=3_Z'kgSGǎl{ѵ-}EO rWa'XD# '@D#h}̷O\4-2O 7JixM>ө|ޫk}]M>3ϧ4Kkn> ՛dN;[RN[/֟vhw8lz-ݶyVz-n=3Oۏ4Ol4{:iIzkO1ռW[ޚn|þiӺ[ݶ>+n>kR~ccm=Ogif)z=ekOEn=J=3Orn<^M'giM=OT4eglx枩4S8r'~BQ!/5%@NXW.ogny枹ɯUy{-U]*2* "v%U*>UP/ @}!!W䶞M6m䶞n>;dO?Ƕx㘉⛞s4x9gSq4Du4m)'љyf4K{~:gћlZۍ4d4M붟,kcۏ4Gcܶ=ǽm=sO~hi^9>ךL=SO4M> M=v5עiݚy&RwDŽϝ7<ַU\9r"!<-'VRⵎ%{-1y%c~ۏn' !#isDqԷ}[4f|3qw>[[q> nn/=q̧t4OGV&xMt3ܛ4n=٧kb{}{i>|m}?uWو $0. l` 330 a[`3333 lfm &mAm0&[`00-B00Ж`kvb$f$ff6-h@$!6 0فf@ HH   HI K1 03 &@`Ř $!mmfi$ m $ X I@! Fi$$[B@ ,Ā I IBɰ}~a~olp'7y|JZ>[i}ҚxM4D]uߺ9iyM<ki1䶧xOq$HI$ f-d{笞)U{N_nj`P2B LʄHD $h煙a7Js i:M:IeqUP;;D:  3RMzժzҝѫ_\7I`@j٧P "]j׫^^so;zK|4Ԫ7(yjU>.{v^9@I,TnYzւ!CF1 nBc)z3ZFe팮Zҕ-dH eeȯ" D}1 . eRy,@YRqF#UX,N4r:݃$|CCCa\#$=G.Ro !wg=Zk>.Yx E݉aXΞ! V ٯMP OH + pTo|~X1@2"cq""X7 ?TDT _4ڵgZ*XU@7>>w-nyٞC24vsP-C/q4Qd߆{IJS+Elc DD@#XyhGp0iug3KU>JU&Wp|#/fd:Dv|lXY8Q-5 xS: $XhdVy p rr/) F. 1@#jV_%늊*կUe(mpD4)k˼6ڡR!2UV6Sє7 f #%M+@;kY1wvvvC61J"|M]zׯd%⩋EA,d46ǑzYЄ,2ŵ,r,Zm!#.@+(("01x竣_o8x|Bh4^|x*\byxnT:bF2|͚2Z- 3\ 1f k[2ԅkOd.ᦚU:gnj`j܎I.@=W¸ߏJS!†aPR7ۅ!)zTn1o {S@],/#*zTV7R@G`(Q)h\i\fYeGϚEmDPvxȉAXmNJSR@H5#bNJtR 0T q5c)8u87ҶJXJ2x+XN#Y:F2 .[A{dnm}qRCmn  74+V d_|tCqu CqGI&ߏ*|!}A6hhu h8 6ӱB ׻?7~\J2!ޥky/z{o7ec>>^y^jf y&[luFej{teeߕ6q˗NriOǖ:axBQl5aFc<44H@X, sqpHtb/|\z 'V%g^^XݘVyfm h+ YrD`m:zU-/[{9lɑq !Z e[sd%&U3!c@ah-fY`eY*o7fv4!:3m!/V% K 3**RTcͅ[(Эex~ul+vX:2Qf3z%@ҫӛ_\*X ؈܀UT׮8 *IRRDl 1XD,,,1 cIm| r @U; ѧ8K^=M l[gkMpB{cU1``jK9)uk[s/ :=g__g_[ %vx^0(ÂB })aelmr۷>12u~zXmLFDcBE5BvϨ8*e}x߅VBv1x)'ĭe]&NmsQ˧\_GW4vDBEzU@3=c)BP}qӧenni={G6ݖ-T(dv`_b(ׂ! QY/3 fQ&z=W_;~5ݻt /q:( @'Y fD3 U*T@YU{"q^L 7 `NEV & NuC_1\`*,yW! E0DpQ( PDRpS 1%)TKbV ])S^.aĠ'jS [97Q;5rB\ʃl, rJ΁o%= f`48o \߳#+Z-x(cq0d z&8 F2'-×1-ݷvFfq}v^xw_xv=3o 1 T:nL+ZvhXj hj PPXXA҃^fjUW" EJ@zl i1huDMl׷dn5a$b̨q1ӵ +Lu􋇘 :ST.ndlQׯ8r˖l6c 5h*m s6k}5hcdB!$!&7UQER5TSfՖ4%S*U BDEHaAAGV%^cu0 d# $^<׻?1PU뵣&fɏ<L`)㔺S7e;p~cJWwax01s 4p 0b@40,s!*֋/u_t 5չNI(Ez Ќt(F{ӎ8\<.͖6ۮF1xn( +FּbB'2@E()j`'73dMYkyӌ-u6,x4PDE$ Eȫ,Fk'&V`sBN+4S*e BD4AZp.꺉QP1J=βQx`0bԶb cA84~*0]ذ4zܨMlw>=>mtmkp^4 0d3`6 0:Z_ ;i/Z`^$# ՃQ`0^AaP2!hac$.l±7MHf@ㅦ2 ֹؾc*X*QVr~0m\(QǏ˟ yy` 0:3 p6 aFe `p GhR4:J˭TDPH E@@3"#rZuc~.8W]5mjK}6%Nؾ.c|^6rc۫п*S΃ÞB10YlT5DBdV {oqf:5bry"ֶW6ܹ{ZUժhk; i/+sؖF-$[%|ő=yX+>(I͋؇^O1螕 P0DXAHD W|D V *f@DE*= 'lB#Ȉ8d1 A @,AG8N+bm!u M  HQDĀ}[Bg=5j̅*5p@cf@X.$5,G[iB#paRR(T ᄨH9R+$, 0~5Y9-Zv>a!dNƵ} pC#""" rY Ba<}o[r͉6[_9´\8rnQ1惚1e'kLT6;7vB )Â1bt\2(.>FI@2h|M}E9I"pɿBaKa1gY 1`HDHx 9D۪3@qll>dn[(zˇMP>:-oPk~Seu.iF rǟFi=;p 8 hp8 ; x D gr1 DJZ\Eҍ[m;;JN- YdM9D.ҮsetIMBV,KZ@(k{t2U>n4*4[c`jϰ,=Pd- x/ @HD(eH;9ҥVn$smQ仍,1=wsqtiJR`` сh1e qvqV^j0 b[z5ca]e~KeZ>׵/c?Ï{D| gg9o䛇˺B9z5C7C8o}hRQOc'WWkGBpsZ7ii? Qà 5Vo ja|"K r=ߔe㫋VO y9@/+G-t!!>$>[F?sx9ok/{s˵&GW҆|ly:guey,Pt"oG//\GLǪDǛjO~?n ݧY)sYu0?7۫>=ҡ9ݮܻvnT bjېG688Bn(l޴nrCrQ÷Cb En4uG#8p6;.!mn')8m8p$9vsô> c8{9sITjsjլwZǼ4XՒյ%0In4̚ 9)ْLZ/dGpbw:! 7y+7)!i&luhc{2)YE1cN*bԒ)➗#u'CXC߰Y%Иd@ddMcb!( %U`}b*W+-+}#K5ln2W̦3x;624م9t7˂iYb}yƚ6]լfמ]EoEϦ+s;˸?O'Pڿ"C: OX~ hD_gpmj>Y |ẗkCkXmyO^S!~/\肇(]j]oE]iؽ, /6~a͔-ynGYDVHia>,ư@p"yo06}Z5{` `Z;|m9ѧ-uky}&4fe"7C7M5 d `;ۈ`7$ذmuX%%G*3lg4m5Ǔ-sV5kƶXz\ˡL0ٮ.HL5S2w:{ יfM*4W"zbmMսXޥﱧ̚^7kVdT%XWTұZH._a)VRc.WBvwj"HX˶ @Dd.TxrjK-BJQ@N :UE #t*R9p$iD%@BH!@$%*APȊ `)4*@@R 4@E@@+QQT j)IRA  * EB@`ꚡQJpTUTE BRMRpI@B RHH%B*B ((J#@m(!%($DDNHT()$ AHRT9R*p#97mQRX`6 4R%'B TP( T@H7B@'I Q@HBd(J0%J)$PQ($`:"BRJ1U tTDPTTT)H2 *iRA I B$ꢪnT*n @`)Q!RQ*HVԐdQR'$NS$ Dd$ QR'*A H E*H)64Q!*lP7!$HQ R NpER*tUPRAHiHQҢ܄MSIԊH6 &R@ܑN$"Β@5b3b362d644e8fa3b359db57be0ff5de8a08365ce9a59cddc3205244a968231e-comps.xml000066400000000000000000000012471372027637700411440ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/02/repodata testgroup false true 1024 Test group Description of test group foobar testcategory 99 Test Category Test category testgroup 65c4f66e2808d328890505c3c2f13bb35a96f457d1c21a6346191c4dc07e6080-updateinfo.xml.gz000066400000000000000000000000671372027637700420450ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/02/repodataQ(K-*ϳU23P)-HI,I-чw4*705a58b0e169bf1d2ade8e4aacc515086644ce16cee971906f920c798c5b17d0-other.sqlite.bz2000066400000000000000000000026661372027637700422650ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/02/repodataBZh51AY&SY}22Ewk8P!BPl^jOITITjhyOd=OQjAdhiH<LC542SF xbz4ѦdhM2LC @2dFLFb144M4LFFL0(44SMOThM 44!zhCA&'MML0^c"ssʾ+Ҋ$)$Dt!B 0B8gT"q :G]3oH{Otl\|5/G޿-hv^ۿЦC+ kchN7 {tCF$z"<˜-bA.ݚ]syV eWi\=s8qO*M.!R~/, ↬ `1!ouf0KJ$jRaA`SDZ4 D4.~17; ҽd}G4 |FeW 0t~un s;tJɴ :) @~>n;CL9L%dBc7!u0ד{=;nYqS}YF)/),,${I>.ㆪJ;"(B? jk&DGf*DQ0ӳ3(`xx3 T"4Q5)Mr!r+l4 4$9@3@BE``'R\BS9i!(Ѐ8KѐI}PRil@jՅܚ1`BP,򗲪kmE""( ^CDڛ) t2$\ TV***/^" `DA$fʪ,_擄MLf'?LgEo+b/P)#IGc,vz˹;,(G)<3 S2wm1_͕33 _PI$i)ciJ ]3)I>LYU Kj)ł3#w -:CJy<:BwrL*LAjWC,f$c[8A`%ppE<4uN"(h7YOS>C 3u&ƪ0+\ʋ =.uDjTWèSAF;C0Q*`!זNJ[ ӤFș6M}Sd:.bf),c5ɚSZ 7˧@w]f1$#9.>$L7%zK xel؟$w)uz`JM .=Vp CEf @Nh ]J;vWgh"Aړ!Il 7ԺKHxGxtn]rvm!C2(lzcכ6mNv8zxdT0dp Iđe͢':TP~+&qZXLb!v0vd9FhƀLH$=Dc34 m5sho+vٷ!%TM4W]4 3$l&dsUpp>=ۑy>@i )JRBRFfi4"i4$e+H#$,@BE?w$S ,c395ae7d8a9117f4e81aa23e37fb9da9865b50917f5f701b50d422875bb0cb14-comps.xml.gz000066400000000000000000000005131372027637700414120ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/02/repodataR]O0}W\,a$1Jxɔnil;" oqƖ_h&SXZ2_m0#K>bWKmV}eaZG@ƔGa]J$(hTiJWHhH':U Vb&L=V[4eծ@LLӮlU6 Mond#Ovh=FU·5eȱPwpAZ;gZaS4S1^;T8̵9<2.!-{b}TP3u|ePc949d2b2371fab1a03d03b41057004caf1133a56e4c9236f63b3163ad358c941-pkgorigins.gz000066400000000000000000000002141372027637700413520ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/02/repodata٪PA!Dѵޥi!-4&z{ /|U'[4H#(k9&: bgF J*V*lf?R)S :@9 qq %>x 1355393568 binary-i386 r test 5a8e6bbb940b151103b3970a26e32b8965da9e90a798b1b80ee4325308149d8d b8d60e74c38b94f255c08c3fe5e10c166dcb52f2c4bfec6cae097a68fdd75e74 1355393567 956 3411 2431efa18b5de6bfddb87da2a526362108226752d46ef3a298cd4bf39ba16b1d afa4a01d7a692ab8105a39fed5535b5011f0c68de0efbc98f9d6ffea36de85fe 1355393567 43338 735112 76b2cfb04531a66e382f187e6a7c90905940d2b2f315b7fd738b839887d83c35 2169e09e2c6c91393d38866c501a8697d0a1d698dd3b1027969dc16d291d8915 1355393567 826 1934 a09c42730c03b0d5defa3fd9213794c49e9bafbc67acdd8d4e87a2adf30b8752 27b2200efa2c518e5dd5a59deb9ab33c2abca74cb74f5241e612b15931dcec37 1355393568 2649 23552 10 5b37f89f9f4474801ec5f23dc30d3d6cf9cf663cb75a6656aaa864a041836ffe 8239ecd9334a3bc4dfa9a242f7c4d545b08451a1ad468458e20f3d3f768652c3 1355393568 23038 200704 10 705a58b0e169bf1d2ade8e4aacc515086644ce16cee971906f920c798c5b17d0 916ca5e879387dc1da51b57266bda28a2569d1773ca6c8ea80abe99d9adb373e 1355393568 1462 8192 10 5b3b362d644e8fa3b359db57be0ff5de8a08365ce9a59cddc3205244a968231e 1355393567 679 c395ae7d8a9117f4e81aa23e37fb9da9865b50917f5f701b50d422875bb0cb14 5b3b362d644e8fa3b359db57be0ff5de8a08365ce9a59cddc3205244a968231e 1355393567 331 679 65c4f66e2808d328890505c3c2f13bb35a96f457d1c21a6346191c4dc07e6080 ded9c95e1b88197c906603b5d9693c579cb0afeade3bc7f8ec6cae06b962477d 1354188048 55 42 c949d2b2371fab1a03d03b41057004caf1133a56e4c9236f63b3163ad358c941 3928c6aadcfdff101f4482db68c0d07f5777b1c7ad8424e41358bc5e87b8465b 1355315696.84 140 364 26e351e1a38eb1524574e86ab130ea4db780aa1a4a8bb741d37595ed203f931c 0052b222add25fed094793c24e73aa07fd598f43f73c1643de26c5e81f6d8c07 1337937059 336 574 32d3307b672abf7356061912fa3dc9b54071c03a75c671111c1c8daf5ed1eb7e 1355335029.48 8a35a38aef926fd88f479f03a9a22e1ab7aa8bd1aeaa9d05cd696f101eee2846 300 492 librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/03/000077500000000000000000000000001372027637700246075ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/03/Archer-3.4.5-6.x86_64.rpm000066400000000000000000000060351372027637700303040ustar00rootroot00000000000000Archer-2:3.4.5-6T>D @ (Ao>KH92dbe81e64b4f75ca4aa9b7bf27d1112b725a650 >@ ?d   )8<TX]a q    W \hw   (8 9:BFGHI(X,YHZt[\]^bd~ltuvwxzefCArcher3.4.56Complex package.Archer packageQb localhost.localdomainISISGPLSterling ArcherDevelopment/Toolshttp://soo_complex_package.eu/linuxx86_64A큤QbQb Qbe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855rootrootrootrootrootrootArcher-3.4.5-6.src.rpmbarabarbbarcbardbareArcherArcher(x86-64)     fooafoobfoocfoodfooefoofrpmlib(CompressedFileNames)rpmlib(FileDigests)rpmlib(PayloadFilesHavePrefix)rpmlib(PayloadIsXz)21.0.0-134563.0.4-14.6.0-14.0-15.2-1 bbabbbbbcbbdbbe22221111.2222.3333-44443333444455554.9.1.3Qb@OO[@Tomas Mlcoch - 3.3.3-3Tomas Mlcoch - 2.2.2-2Tomas Mlcoch - 1.1.1-1- 3. changelog.- That was totally ninja!- First changelog.aaaaabaacaadaaelocalhost.localdomain 1365416480poplpw 2211.22.33-443344552:3.4.5-62:3.4.5-6 222111.2.3-4333444555complex_aArcher-3.4.5README/usr/bin//usr/share/doc//usr/share/doc/Archer-3.4.5/-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=genericcpiox86_64-redhat-linux-gnudirectoryemptylrr.p먎)ߧxz2?7zXZ !#,] c2 1O8۟Wџ+̼u0桶ϋ_= hg֞uy_:_ `յń__GMi \{Rؚ&N˳ʋ 'jТbJ#Pz kFނ" W^u=~:mI YZlibrepo-1.12.1/tests/python/tests/servermock/yum_mock/static/03/repodata/000077500000000000000000000000001372027637700264065ustar00rootroot000000000000002f0db0b18f1eaa395523aa33561764f6bf24ba54c520e789f778b25339fbce7b-primary.sqlite.bz2000066400000000000000000000040001372027637700426010ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/03/repodataBZh91AY&SY<3o}vH1-n53`/:4)M42&1=#F LL &0i`L4@5R"jG5?Sēh&AёF! `iA&U7 A&CC M  4b dF&4ɑ!h FLdbdh 11da CFhdѐ4ɣC&L21242h0$H 0'P2M5DCj=!hh4 Q驵=Nԙ*G:0E5 -[.y9ޅچe# ٫mO Ѕѣ&Mg`x~7*DJ$)(4V[ą1|37w XF@Ӣf@Ou3bF2eh`$b :F_frss5kfq\sސh~!Xa3N${cbs.ZKmb NsS$$+^xl'S^k5rv|bh J "FB_60cU&yיPkTl6װ*O4W + ;EEsΠmjBiRrAj!ub-_wh<}IٹĮXb5)R4>ߟn91wm^@ҭU]uv|p,{ LeA~Mw$HRv/[\="UPulES$}Y]A kL?_eU#_ʏZ$/3X3`{\BJ3SM4JFH +k;)~P6nw07Cct`<3|s xGRvثz1Of=Y}{0m-'Պ##6 a9m$I/dY! Ƣ`0d)s*Cx&6_ E pTm'9#)Ց#C*O<26LŃ*b swѰ<SƤR$PjTJIy9HuPְ"6|mŢ‚- ,S! _bZPxߐlnGyKK+op@+6Id EZXKvv;20B)&(LRCo $Lh L)E@F?* w- ou*˂YCF"m!FgZ( K4 VP֢!jEuEr .JY.7v&X6ޛ*&kj$N DYmd9 NӐ&DD:٣{T |Wv\jcE54lF2ŏlgx;q''!(,+p ;XK PABsB\6!/s\p45Mڂ'Mm`rVG00@&PniV-שʗ,[7C:BH )G#K8|SGA%qᯞF1VVi`2ǵ'CVW{@,=P6~XeY0BFXS>˩v!K**tE 0.ߗV^c5nV4\v5Ky'P{xrCVD`GC-#Q3|l=MEO[)S1/U5ak᮲Hhĥ:却S'SpYVB -p[Sh*82=zi BRn!0CX*1+ډ6fNGBNiJ"9$Gj]s0Ÿ-ӋqAI4w$S q0e7ed309eed4bffaa72df0933718783b4f87241e96a7f105172789db9805927ec-filelists.xml.gz000066400000000000000000000004441372027637700422560ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/03/repodata}Pn0!6T(RӭKΑ 2Pѿ/i N޻{/mC0 Ao:Y&}W @\Ʊq~#;hzkS3.H쬂Y)sfXM\YK23jqTˤL"ԙ-*8sD/UHs7sn]qX*FIQ ^@Kھp = wh]BZ{K^librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/03/repodata/repomd.xml000066400000000000000000000056231372027637700304240ustar00rootroot00000000000000 1378283288 e7ed309eed4bffaa72df0933718783b4f87241e96a7f105172789db9805927ec f7f5c79dfba8ec3b1fc5a72ac5ffff88ee37f510ca67cd5a6c997bfa5d13ec0f 1378283288 292 428 62ab4021c1ef6be14e14ebfeb3d6b8c6fca6a9d2f70eefad355641cf3cb14741 224a8e48bf3900ae7ec20029581c500e1eaa10ac8a3dccc91cf238ca6623859c 1378283288 836 2709 2f0db0b18f1eaa395523aa33561764f6bf24ba54c520e789f778b25339fbce7b 0ae13f57b3523cd7c9e30ab5bedcd641c1e5cc34af6407393aec029b952e1d92 1378283291 10 2048 21504 b1ef4b4606483c7d741d2a3cedae6c89cf1c9201a17ffbe7d920b6f5a73b1744 cb60147bc6113023ecffe20215acd40e1674c14e524b3a179a87409ac7738276 1378283289 10 827 6144 971c0c7cac60d455ed69be21537bfa31c42cf1c41442ec8b5df0457d62f33c2d 089af1def4d78096b74dd5b5f40790bc8f582cb34979e55def1cc44fe04d328e 1378283288 358 653 76b5b88e17d2fc404a2e3fe8ee229f0f5aba5c2b80359e998382b5ba2ea4f406 b37f3662c77ae2b23df44d8376afb08f45a0bad1001dbd85154771294297bfb3 1378283290 10 823 7168 librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/04/000077500000000000000000000000001372027637700246105ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/04/empty-0-0.src.rpm000066400000000000000000000027201372027637700275470ustar00rootroot00000000000000empty-0-0T>D @nIuO2cd068653a6a35807cb304ad20448cdf091bde8f>'e?Ud   ,0 5AGPT V X \  (GH I\]^#$d(e-f2F4Cempty00""Ohlocalhost.localdomainLGPLUnspecifiedlinuxx86_64Oh3dc81ed5769274941e37f5c60424f453b943ba55151a06afb07e38cbdd62845d tmlcochtmlcoch  rpmlib(FileDigests)rpmlib(CompressedFileNames)4.6.0-13.0.4-14.9.1.3> 4empty.speccpiogzip9localhost.localdomain 1340696582?PM P켿"y+$¢SW{ȧ}O3\vwfvm@$d t +^z:ƜҢj6ppa) *%ϳA@xHOpӔ:PsyI]#aɋJPSLVLՅn\l'yj&w5MkzZslibrepo-1.12.1/tests/python/tests/servermock/yum_mock/static/04/repodata/000077500000000000000000000000001372027637700264075ustar00rootroot0000000000000023ff21cb3eb3f3f1b0ed19f8ce28106a6576ccd67ea41cd876dc947fa1263242-other.sqlite.bz2000066400000000000000000000012251372027637700424040ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/04/repodataBZh91AY&SYQ!wh8(@kkF4")idhh`C @ɴ42= IѤ(dP4&A@ɠdI2%y22m xOPzji8ʠi+,"W'' @5 jr]'p-,],U v.qI Br*S8h1 NY͚KIO/ 0L/7DK:G{a.xY R u)?HDJfLN!:m! P)LR`F>ŗ nb>rz@Hd@RM2UqP>jH8q ZEլ$RqD,F["h!nĭ {2;;L,䀃.p l85580348d2b8f6f137394118878577ff062b4582ed8c3887816915a45a5aa9db-filelists.xml.gz000066400000000000000000000003531372027637700416250ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/04/repodata=OAn06b"Ln}AwI,XTkivG39Oo*$g zڿuhr1E>H)\ߞn#)Bsp|xv:WƻCk%벡.(m[5!Z U="EڡU- ypC V0<6b;1 r9piGqP@; F?ʺ=4862c129de8258d7058589dd2ff8f452039ca008e5e462238a72be550f6725aea-primary.xml.gz000066400000000000000000000007551372027637700415700ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/04/repodataSM ϯGQSʜv2ڝ _0:m/k /5+X'pFH}]_K3 V֜sVIŞ)ȠHo-ْ,éQ[ pɇiK]aF~c7Z^*@C'YyEi>=4 ؃{_γq$@5~  g04X;,c$l zc#QrϏ<30<1gk~j7q`1dA(&=!z:3[=' ;ta w8W$ٺKŦH %lb8d9a1e82bee3c633f801432220609e3622f7c5b5d4da9dceffab33ab1cf02ebc-filelists.sqlite.bz2000066400000000000000000000013201372027637700434530ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/04/repodataBZh91AY&SY_EDoh 8"` @Wq hi@L 0@bi4F'š@4&6iz4h44dJ5 Tʞ4)G2=!=OPMjy#+(zz}Q**1TTOP$DDDn rm%r"qÖ1g㘝H"?76 ,"3 ';9pf_0> "i|+p4dT )a Vugؕ--Ud"C犹H` XW߲@bψt#l=Bl I`ƉjPB'g`xN<(PϩFNe\-Y܏AĺrZɍlվzDmkɯ[.b " "%I8;J6 *BײG 1407756015 85580348d2b8f6f137394118878577ff062b4582ed8c3887816915a45a5aa9db b310e5ad7544f24f26b4b8488541b0e23747cd38c41c83313fac6de040ad0312 1407756015 235 308 862c129de8258d7058589dd2ff8f452039ca008e5e462238a72be550f6725aea 85db7a83045cc6e6e5ab8450104a0bcf02634c0416894836af7cd0445a515808 1407756015 493 866 9dfc1aa118abeb734358e583aeeb251d850485bb763dae65ce7a1e29cacc269c d03ca80a3637daa7780e9415bc4688d5440f5a2003056bbb3061d2a8633d5d04 1407756017 10 1375 21504 23ff21cb3eb3f3f1b0ed19f8ce28106a6576ccd67ea41cd876dc947fa1263242 49db2e6134fbb0673f0ab06114ef35f83a5cd09de9f1221ded66f089fa94dca9 1407756016 10 661 6144 c9a57bff45270c41f9d707c0e42003ee0a37d2e966a72d9dc1f8836f0f9d32e3 07c5c004d02ab6f0d7235f35e022f3bad1d91a9cbf97daaaf12d91bd77437686 1407756015 223 278 8d9a1e82bee3c633f801432220609e3622f7c5b5d4da9dceffab33ab1cf02ebc 3d33daf35281f7520e248dd01b71a04904ac5450617d25d1f8e7c47fc616cf23 1407756016 10 720 7168 librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/metalink/000077500000000000000000000000001372027637700261715ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/metalink/badchecksum.xml000066400000000000000000000012021372027637700311570ustar00rootroot00000000000000 1347459931 2621 xxxxxxxxxx4c21945257d9041a908d0d01d2ca16 http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/repodata/repomd.xml librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/metalink/badfilename.xml000066400000000000000000000012001372027637700311330ustar00rootroot00000000000000 1347459931 2621 75125e73304c21945257d9041a908d0d01d2ca16 http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/repodata/repomd.xml librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/metalink/badfirsthost.xml000066400000000000000000000021641372027637700314120ustar00rootroot00000000000000 1347459931 2621 f76409f67a84bcd516131d5cc98e57e1 75125e73304c21945257d9041a908d0d01d2ca16 bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0 http://256.0.0.1/foo/bar/bad_url http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/repodata/repomd.xml librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/metalink/badfirsturl.xml000066400000000000000000000021721372027637700312360ustar00rootroot00000000000000 1347459931 2621 f76409f67a84bcd516131d5cc98e57e1 75125e73304c21945257d9041a908d0d01d2ca16 bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0 http://127.0.0.1:44444/foo/bar/bad_url http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/repodata/repomd.xml librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/metalink/firsturlhascorruptedfiles.xml000066400000000000000000000022631372027637700342370ustar00rootroot00000000000000 1347459931 2621 f76409f67a84bcd516131d5cc98e57e1 75125e73304c21945257d9041a908d0d01d2ca16 bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0 http://127.0.0.1:{PORT_PLACEHOLDER}/yum/harm_checksum/primary.xml/static/01/repodata/repomd.xml http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/repodata/repomd.xml librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/metalink/good_01.xml000066400000000000000000000020311372027637700301370ustar00rootroot00000000000000 1347459931 2621 f76409f67a84bcd516131d5cc98e57e1 75125e73304c21945257d9041a908d0d01d2ca16 bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0 http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/repodata/repomd.xml librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/metalink/good_02.xml000066400000000000000000000012021372027637700301370ustar00rootroot00000000000000 1347459931 2621 75125e73304c21945257d9041a908d0d01d2ca16 http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/repodata/repomd.xml librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/metalink/metalink_with_alternates.xml000066400000000000000000000032251372027637700337760ustar00rootroot00000000000000 1381706941 4761 bad bad bad bad 1347459931 2621 f76409f67a84bcd516131d5cc98e57e1 75125e73304c21945257d9041a908d0d01d2ca16 bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0 123 456 foobar http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/repodata/repomd.xml librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/metalink/mirrors_01.xml000066400000000000000000000015411372027637700307110ustar00rootroot00000000000000 http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/repodata/repomd.xml http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/03/repodata/repomd.xml http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/04/repodata/repomd.xml librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/metalink/nourls.xml000066400000000000000000000010511372027637700302320ustar00rootroot00000000000000 1347459931 2621 xxxxxxxxxx4c21945257d9041a908d0d01d2ca16 librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/metalink/varsub.xml000066400000000000000000000020371372027637700302170ustar00rootroot00000000000000 1347459931 2621 f76409f67a84bcd516131d5cc98e57e1 75125e73304c21945257d9041a908d0d01d2ca16 bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0 http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/$version/repodata/repomd.xml librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/mirrorlist/000077500000000000000000000000001372027637700265735ustar00rootroot00000000000000librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/mirrorlist/badfirsturl000066400000000000000000000001631372027637700310370ustar00rootroot00000000000000# lalalalalala http://127.0.0.1:44444/foo/bar/bad_url http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/ # coments librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/mirrorlist/firsturlhascorruptedfiles000066400000000000000000000002311372027637700340330ustar00rootroot00000000000000# lalalalalala http://127.0.0.1:{PORT_PLACEHOLDER}/yum/harm_checksum/primary.xml/static/01/ http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/ # coments librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/mirrorlist/good_01000066400000000000000000000000631372027637700277450ustar00rootroot00000000000000http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/ librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/mirrorlist/good_02000066400000000000000000000002121372027637700277420ustar00rootroot00000000000000# lalalalalala http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/ # lalala # xxx://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/ # coments librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/mirrorlist/nourls000066400000000000000000000001271372027637700300400ustar00rootroot00000000000000# lalalalalala # lalala # xxx://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/01/ # coments librepo-1.12.1/tests/python/tests/servermock/yum_mock/static/mirrorlist/varsub000066400000000000000000000001041372027637700300130ustar00rootroot00000000000000# varsub http://127.0.0.1:{PORT_PLACEHOLDER}/yum/static/$version/ librepo-1.12.1/tests/python/tests/servermock/yum_mock/yum_mock.py000066400000000000000000000103251372027637700252740ustar00rootroot00000000000000import os from flask import Blueprint, render_template, abort, send_file, request, Response from flask import current_app from functools import wraps from .config import AUTH_USER, AUTH_PASS yum_mock = Blueprint('yum_mock', __name__, template_folder='templates', static_folder='static') @yum_mock.route('/static/mirrorlist/') def serve_mirrorlists_with_right_port(path): try: with yum_mock.open_resource('static/mirrorlist/'+path) as f: data = f.read() data = data.decode('utf-8') data = data.replace(":{PORT_PLACEHOLDER}", ":%d" % current_app._librepo_port) return data except IOError: # File probably doesn't exist or we can't read it abort(404) @yum_mock.route('/static/metalink/') def serve_metalinks_with_right_port(path): try: with yum_mock.open_resource('static/metalink/'+path) as f: data = f.read() data = data.decode('utf-8') data = data.replace(":{PORT_PLACEHOLDER}", ":%d" % current_app._librepo_port) return data except IOError: # File probably doesn't exist or we can't read it abort(404) @yum_mock.route('/harm_checksum//') def harm_checksum(keyword, path): """Append two newlines to content of a file (from the static dir) with specified keyword in the filename. If the filename doesn't contain the keyword, content of the file is returnen unchanged.""" if "static/" not in path: # Support changing only files from static directory abort(400) path = path[path.find("static/"):] try: with yum_mock.open_resource(path) as f: data = f.read() if keyword in os.path.basename(path): return "%s\n\n" %data return data except IOError: # File probably doesn't exist or we can't read it abort(404) @yum_mock.route("/not_found//") def not_found(keyword, path): """For each file containing keyword in the filename, http status code 404 will be returned""" if "static/" not in path: abort(400) path = path[path.find("static/"):] try: with yum_mock.open_resource(path) as f: data = f.read() if keyword in os.path.basename(path): abort(404) return data except IOError: # File probably doesn't exist or we can't read it abort(404) @yum_mock.route("/badurl/") def badurl(path): """Just return 404 for each url with this prefix""" abort(404) @yum_mock.route("/badgpg/") def badgpg(path): """Instead of /repomd.xml.asc returns content of /repomd.xml.asc.bad""" if "static/" not in path: abort(400) path = path[path.find("static/"):] if path.endswith("repomd.xml.asc"): path = path + ".bad" try: with yum_mock.open_resource(path) as f: return f.read() except IOError: # File probably doesn't exist or we can't read it abort(404) # Basic Auth def check_auth(username, password): """This function is called to check if a username / password combination is valid. """ return username == AUTH_USER and password == AUTH_PASS def authenticate(): """Sends a 401 response that enables basic auth""" return Response( 'Could not verify your access level for that URL.\n' 'You have to login with proper credentials', 401, {'WWW-Authenticate': 'Basic realm="Login Required"'}) def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): auth = request.authorization if not auth or not check_auth(auth.username, auth.password): return authenticate() return f(*args, **kwargs) return decorated @yum_mock.route("/auth_basic/") @requires_auth def secret_repo_basic_auth(path): """Page secured with basic HTTP auth User: admin Password: secret""" if "static/" not in path: abort(400) path = path[path.find("static/"):] try: with yum_mock.open_resource(path) as f: data = f.read() return data except IOError: abort(404) librepo-1.12.1/tests/python/tests/test_handle.py000066400000000000000000000546041372027637700217500ustar00rootroot00000000000000import unittest import librepo def foo_cb(data, total_to_download, downloaded): pass def foo_hmfcb(data, msg, url, metadata): pass class TestCaseHandle(unittest.TestCase): def test_handle_exceptions(self): h = librepo.Handle() self.assertTrue(h) self.assertRaises(AttributeError, getattr, h, 'foobar_attr') self.assertRaises(AttributeError, setattr, h, 'foobar_attr', 'xyz') self.assertRaises(ValueError, h.getinfo, 999999) self.assertRaises(ValueError, h.setopt, 999999, 'xyz') def test_handle_setopt_getinfo(self): """No exception should be raised.""" h = librepo.Handle() self.assertFalse(h.getinfo(librepo.LRI_UPDATE)) h.setopt(librepo.LRO_UPDATE, True) self.assertTrue(h.getinfo(librepo.LRI_UPDATE)) h.setopt(librepo.LRO_UPDATE, False) self.assertFalse(h.getinfo(librepo.LRI_UPDATE)) h.setopt(librepo.LRO_UPDATE, 1) self.assertTrue(h.getinfo(librepo.LRI_UPDATE)) h.setopt(librepo.LRO_UPDATE, 0) self.assertFalse(h.getinfo(librepo.LRI_UPDATE)) self.assertEqual(h.getinfo(librepo.LRI_URLS), []) h.setopt(librepo.LRO_URLS, ["http://foo"]) self.assertEqual(h.getinfo(librepo.LRI_URLS), ["http://foo"]) h.setopt(librepo.LRO_URLS, [""]) self.assertEqual(h.getinfo(librepo.LRI_URLS), [""]) h.setopt(librepo.LRO_URLS, None) self.assertEqual(h.getinfo(librepo.LRI_URLS), []) self.assertEqual(h.getinfo(librepo.LRI_MIRRORLIST), None) h.setopt(librepo.LRO_MIRRORLIST, "http://ml") self.assertEqual(h.getinfo(librepo.LRI_MIRRORLIST), "http://ml") h.setopt(librepo.LRO_MIRRORLIST, "") self.assertEqual(h.getinfo(librepo.LRI_MIRRORLIST), "") h.setopt(librepo.LRO_MIRRORLIST, None) self.assertEqual(h.getinfo(librepo.LRI_MIRRORLIST), None) self.assertFalse(h.getinfo(librepo.LRI_LOCAL)) h.setopt(librepo.LRO_LOCAL, 1) self.assertTrue(h.getinfo(librepo.LRI_LOCAL)) h.setopt(librepo.LRO_LOCAL, 0) self.assertFalse(h.getinfo(librepo.LRI_LOCAL)) self.assertFalse(h.getinfo(librepo.LRI_PROGRESSCB)) h.setopt(librepo.LRO_PROGRESSCB, foo_cb) self.assertEqual(h.getinfo(librepo.LRI_PROGRESSCB), foo_cb) h.setopt(librepo.LRO_PROGRESSCB, None) self.assertFalse(h.getinfo(librepo.LRI_PROGRESSCB)) data = {'a':'foo'} self.assertFalse(h.getinfo(librepo.LRI_PROGRESSDATA)) h.setopt(librepo.LRO_PROGRESSDATA, data) self.assertEqual(h.getinfo(librepo.LRI_PROGRESSDATA), data) h.setopt(librepo.LRO_PROGRESSDATA, None) self.assertFalse(h.getinfo(librepo.LRI_PROGRESSDATA)) self.assertEqual(h.getinfo(librepo.LRI_DESTDIR), None) h.setopt(librepo.LRO_DESTDIR, "foodir") self.assertEqual(h.getinfo(librepo.LRI_DESTDIR), "foodir") h.setopt(librepo.LRO_DESTDIR, None) self.assertEqual(h.getinfo(librepo.LRI_DESTDIR), None) h.setopt(librepo.LRO_DESTDIR, "") self.assertEqual(h.getinfo(librepo.LRI_DESTDIR), "") self.assertEqual(h.getinfo(librepo.LRI_USERAGENT), None) h.setopt(librepo.LRO_USERAGENT, "librepo/0.0") self.assertEqual(h.getinfo(librepo.LRI_USERAGENT), "librepo/0.0") h.setopt(librepo.LRO_USERAGENT, None) self.assertEqual(h.getinfo(librepo.LRI_USERAGENT), None) h.setopt(librepo.LRO_USERAGENT, "") self.assertEqual(h.getinfo(librepo.LRI_USERAGENT), "") self.assertEqual(h.getinfo(librepo.LRI_YUMDLIST), None) self.assertEqual(h.getinfo(librepo.LRI_RPMMDDLIST), None) h.setopt(librepo.LRO_YUMDLIST, ["primary", "other"]) self.assertEqual(h.getinfo(librepo.LRI_YUMDLIST), ["primary", "other"]) self.assertEqual(h.getinfo(librepo.LRI_RPMMDDLIST), ["primary", "other"]) h.setopt(librepo.LRO_YUMDLIST, []) self.assertEqual(h.getinfo(librepo.LRI_YUMDLIST), []) self.assertEqual(h.getinfo(librepo.LRI_RPMMDDLIST), []) h.setopt(librepo.LRO_YUMDLIST, [None]) self.assertEqual(h.getinfo(librepo.LRI_YUMDLIST), []) self.assertEqual(h.getinfo(librepo.LRI_RPMMDDLIST), []) h.setopt(librepo.LRO_YUMDLIST, None) self.assertEqual(h.getinfo(librepo.LRI_YUMDLIST), None) self.assertEqual(h.getinfo(librepo.LRI_RPMMDDLIST), None) h.setopt(librepo.LRO_RPMMDDLIST, ["primary", "other"]) self.assertEqual(h.getinfo(librepo.LRI_YUMDLIST), ["primary", "other"]) self.assertEqual(h.getinfo(librepo.LRI_RPMMDDLIST), ["primary", "other"]) self.assertEqual(h.getinfo(librepo.LRI_YUMBLIST), None) self.assertEqual(h.getinfo(librepo.LRI_RPMMDBLIST), None) h.setopt(librepo.LRO_YUMBLIST, ["primary", "other"]) self.assertEqual(h.getinfo(librepo.LRI_YUMBLIST), ["primary", "other"]) self.assertEqual(h.getinfo(librepo.LRI_RPMMDBLIST), ["primary", "other"]) h.setopt(librepo.LRO_YUMBLIST, []) self.assertEqual(h.getinfo(librepo.LRI_YUMBLIST), []) self.assertEqual(h.getinfo(librepo.LRI_RPMMDBLIST), []) h.setopt(librepo.LRO_YUMBLIST, [None]) self.assertEqual(h.getinfo(librepo.LRI_YUMBLIST), []) self.assertEqual(h.getinfo(librepo.LRI_RPMMDBLIST), []) self.assertEqual(h.getinfo(librepo.LRI_MAXMIRRORTRIES), 0) h.setopt(librepo.LRO_MAXMIRRORTRIES, 1) self.assertEqual(h.getinfo(librepo.LRI_MAXMIRRORTRIES), 1) h.setopt(librepo.LRO_MAXMIRRORTRIES, None) self.assertEqual(h.getinfo(librepo.LRI_MAXMIRRORTRIES), 0) self.assertEqual(h.getinfo(librepo.LRI_VARSUB), None) h.setopt(librepo.LRO_VARSUB, [("bar", "foo")]) self.assertEqual(h.getinfo(librepo.LRI_VARSUB), [("bar", "foo")]) h.setopt(librepo.LRO_VARSUB, None) self.assertEqual(h.getinfo(librepo.LRI_VARSUB), None) self.assertEqual(h.getinfo(librepo.LRI_FASTESTMIRROR), False) h.setopt(librepo.LRO_FASTESTMIRROR, True) self.assertEqual(h.getinfo(librepo.LRI_FASTESTMIRROR), True) h.setopt(librepo.LRO_FASTESTMIRROR, False) self.assertEqual(h.getinfo(librepo.LRI_FASTESTMIRROR), False) self.assertEqual(h.getinfo(librepo.LRI_LOWSPEEDTIME), 30) h.setopt(librepo.LRO_LOWSPEEDTIME, 120) self.assertEqual(h.getinfo(librepo.LRI_LOWSPEEDTIME), 120) h.setopt(librepo.LRO_LOWSPEEDTIME, None) self.assertEqual(h.getinfo(librepo.LRI_LOWSPEEDTIME), 30) self.assertEqual(h.getinfo(librepo.LRI_LOWSPEEDLIMIT), 1000) h.setopt(librepo.LRO_LOWSPEEDLIMIT, 123) self.assertEqual(h.getinfo(librepo.LRI_LOWSPEEDLIMIT), 123) h.setopt(librepo.LRO_LOWSPEEDLIMIT, None) self.assertEqual(h.getinfo(librepo.LRI_LOWSPEEDLIMIT), 1000) self.assertFalse(h.getinfo(librepo.LRI_HMFCB)) h.setopt(librepo.LRO_HMFCB, foo_hmfcb) self.assertEqual(h.getinfo(librepo.LRI_HMFCB), foo_hmfcb) h.setopt(librepo.LRO_HMFCB, None) self.assertFalse(h.getinfo(librepo.LRI_HMFCB)) self.assertTrue(h.getinfo(librepo.LRI_SSLVERIFYPEER)) h.setopt(librepo.LRO_SSLVERIFYPEER, 0) self.assertEqual(h.getinfo(librepo.LRI_SSLVERIFYPEER), False) h.setopt(librepo.LRO_SSLVERIFYPEER, None) self.assertTrue(h.getinfo(librepo.LRI_SSLVERIFYPEER)) self.assertTrue(h.getinfo(librepo.LRI_SSLVERIFYHOST)) h.setopt(librepo.LRO_SSLVERIFYHOST, 0) self.assertEqual(h.getinfo(librepo.LRI_SSLVERIFYHOST), False) h.setopt(librepo.LRO_SSLVERIFYHOST, None) self.assertTrue(h.getinfo(librepo.LRI_SSLVERIFYHOST)) self.assertEqual(h.getinfo(librepo.LRI_SSLCLIENTCERT), None) h.setopt(librepo.LRO_SSLCLIENTCERT, "/etc/cert.pem") self.assertEqual(h.getinfo(librepo.LRI_SSLCLIENTCERT), "/etc/cert.pem") h.setopt(librepo.LRO_SSLCLIENTCERT, "") self.assertEqual(h.getinfo(librepo.LRI_SSLCLIENTCERT), "") h.setopt(librepo.LRO_SSLCLIENTCERT, None) self.assertEqual(h.getinfo(librepo.LRI_SSLCLIENTCERT), None) self.assertEqual(h.getinfo(librepo.LRI_SSLCLIENTKEY), None) h.setopt(librepo.LRO_SSLCLIENTKEY, "/etc/cert.key") self.assertEqual(h.getinfo(librepo.LRI_SSLCLIENTKEY), "/etc/cert.key") h.setopt(librepo.LRO_SSLCLIENTKEY, "") self.assertEqual(h.getinfo(librepo.LRI_SSLCLIENTKEY), "") h.setopt(librepo.LRO_SSLCLIENTKEY, None) self.assertEqual(h.getinfo(librepo.LRI_SSLCLIENTKEY), None) self.assertEqual(h.getinfo(librepo.LRI_SSLCACERT), None) h.setopt(librepo.LRO_SSLCACERT, "/etc/ca.pem") self.assertEqual(h.getinfo(librepo.LRI_SSLCACERT), "/etc/ca.pem") h.setopt(librepo.LRO_SSLCACERT, "") self.assertEqual(h.getinfo(librepo.LRI_SSLCACERT), "") h.setopt(librepo.LRO_SSLCACERT, None) self.assertEqual(h.getinfo(librepo.LRI_SSLCACERT), None) self.assertEqual(h.getinfo(librepo.LRI_IPRESOLVE), librepo.IPRESOLVE_WHATEVER) h.setopt(librepo.LRO_IPRESOLVE, librepo.IPRESOLVE_V6) self.assertEqual(h.getinfo(librepo.LRI_IPRESOLVE), librepo.IPRESOLVE_V6) h.setopt(librepo.LRO_IPRESOLVE, None) self.assertEqual(h.getinfo(librepo.LRI_IPRESOLVE), librepo.IPRESOLVE_WHATEVER) self.assertEqual(h.getinfo(librepo.LRI_ALLOWEDMIRRORFAILURES), 4) h.setopt(librepo.LRO_ALLOWEDMIRRORFAILURES, 1) self.assertEqual(h.getinfo(librepo.LRI_ALLOWEDMIRRORFAILURES), 1) h.setopt(librepo.LRO_ALLOWEDMIRRORFAILURES, None) self.assertEqual(h.getinfo(librepo.LRI_ADAPTIVEMIRRORSORTING), 1) h.setopt(librepo.LRO_ADAPTIVEMIRRORSORTING, 0) self.assertEqual(h.getinfo(librepo.LRI_ADAPTIVEMIRRORSORTING), 0) h.setopt(librepo.LRO_ADAPTIVEMIRRORSORTING, None) self.assertEqual(h.getinfo(librepo.LRI_ADAPTIVEMIRRORSORTING), 1) self.assertEqual(h.getinfo(librepo.LRI_GNUPGHOMEDIR), None) h.setopt(librepo.LRO_GNUPGHOMEDIR, "/tmp/keyring/") self.assertEqual(h.getinfo(librepo.LRI_GNUPGHOMEDIR), "/tmp/keyring/") h.setopt(librepo.LRO_GNUPGHOMEDIR, None) self.assertEqual(h.getinfo(librepo.LRI_GNUPGHOMEDIR), None) h.setopt(librepo.LRO_GNUPGHOMEDIR, "") self.assertEqual(h.getinfo(librepo.LRI_GNUPGHOMEDIR), "") self.assertEqual(h.getinfo(librepo.LRI_FASTESTMIRRORTIMEOUT), 2.0) h.setopt(librepo.LRO_FASTESTMIRRORTIMEOUT, 32.256) self.assertEqual(h.getinfo(librepo.LRI_FASTESTMIRRORTIMEOUT), 32.256) h.setopt(librepo.LRO_FASTESTMIRRORTIMEOUT, None) self.assertEqual(h.getinfo(librepo.LRI_FASTESTMIRRORTIMEOUT), 2.0) self.assertEqual(h.getinfo(librepo.LRI_HTTPHEADER), None) h.setopt(librepo.LRO_HTTPHEADER, ["Accept: text/xml", "charsets: utf-8"]) self.assertEqual(h.getinfo(librepo.LRI_HTTPHEADER), ["Accept: text/xml", "charsets: utf-8"]) h.setopt(librepo.LRO_HTTPHEADER, None) self.assertEqual(h.getinfo(librepo.LRI_HTTPHEADER), None) self.assertEqual(h.getinfo(librepo.LRI_HTTPAUTHMETHODS), librepo.LR_AUTH_BASIC) h.setopt(librepo.LRO_HTTPAUTHMETHODS, librepo.LR_AUTH_DIGEST) self.assertEqual(h.getinfo(librepo.LRI_HTTPAUTHMETHODS), librepo.LR_AUTH_DIGEST) h.setopt(librepo.LRO_HTTPAUTHMETHODS, None) self.assertEqual(h.getinfo(librepo.LRI_HTTPAUTHMETHODS), librepo.LR_AUTH_BASIC) self.assertEqual(h.getinfo(librepo.LRI_PROXYAUTHMETHODS), librepo.LR_AUTH_BASIC) h.setopt(librepo.LRO_PROXYAUTHMETHODS, librepo.LR_AUTH_DIGEST) self.assertEqual(h.getinfo(librepo.LRI_PROXYAUTHMETHODS), librepo.LR_AUTH_DIGEST) h.setopt(librepo.LRO_PROXYAUTHMETHODS, None) self.assertEqual(h.getinfo(librepo.LRI_PROXYAUTHMETHODS), librepo.LR_AUTH_BASIC) self.assertEqual(h.getinfo(librepo.LRI_FTPUSEEPSV), 1) h.setopt(librepo.LRO_FTPUSEEPSV, 0) self.assertEqual(h.getinfo(librepo.LRI_FTPUSEEPSV), 0) h.setopt(librepo.LRO_FTPUSEEPSV, None) self.assertEqual(h.getinfo(librepo.LRI_FTPUSEEPSV), 1) def test_handle_setget_attr(self): """No exception should be raised.""" h = librepo.Handle() self.assertFalse(h.update) h.update = True self.assertTrue(h.update) h.update = False self.assertFalse(h.update) h.update = 1 self.assertTrue(h.update) h.update = 0 self.assertFalse(h.update) self.assertEqual(h.urls, []) h.urls = "http://foo" self.assertEqual(h.urls, ["http://foo"]) h.urls = "" self.assertEqual(h.urls, [""]) h.urls = None self.assertEqual(h.urls, []) self.assertEqual(h.mirrorlist, None) h.mirrorlist = "http://ml" self.assertEqual(h.mirrorlist, "http://ml") h.mirrorlist = "" self.assertEqual(h.mirrorlist, "") h.mirrorlist = None self.assertEqual(h.mirrorlist, None) self.assertFalse(h.local) h.local = 1 self.assertTrue(h.local) h.local = 0 self.assertFalse(h.local) self.assertFalse(h.progresscb) h.progresscb = foo_cb self.assertEqual(h.progresscb, foo_cb) h.progresscb = None self.assertFalse(h.progresscb) data = {'a':'foo'} self.assertFalse(h.progressdata) h.progressdata = data self.assertEqual(h.progressdata, data) h.progressdata = None self.assertFalse(h.progressdata) self.assertEqual(h.destdir, None) h.destdir = "foodir" self.assertEqual(h.destdir, "foodir") h.destdir = None self.assertEqual(h.destdir, None) h.destdir = "" self.assertEqual(h.destdir, "") self.assertEqual(h.useragent, None) h.useragent = "librepo/0.0" self.assertEqual(h.useragent, "librepo/0.0") h.useragent = None self.assertEqual(h.useragent, None) h.useragent = "" self.assertEqual(h.useragent, "") self.assertEqual(h.yumdlist, None) h.yumdlist = ["primary", "other"] self.assertEqual(h.yumdlist, ["primary", "other"]) h.yumdlist = [] self.assertEqual(h.yumdlist, []) h.yumdlist = [None] self.assertEqual(h.yumdlist, []) self.assertEqual(h.yumblist, None) h.yumblist = ["primary", "other"] self.assertEqual(h.yumblist, ["primary", "other"]) h.yumblist = [] self.assertEqual(h.yumblist, []) h.yumblist = [None] self.assertEqual(h.yumblist, []) self.assertEqual(h.maxmirrortries, 0) h.maxmirrortries = 1 self.assertEqual(h.maxmirrortries, 1) h.maxmirrortries = None self.assertEqual(h.maxmirrortries, 0) self.assertEqual(h.varsub, None) h.varsub = [("foo", "bar")] self.assertEqual(h.varsub, [("foo", "bar")]) h.varsub = None self.assertEqual(h.varsub, None) self.assertEqual(h.fastestmirror, False) h.fastestmirror = True self.assertEqual(h.fastestmirror, True) h.fastestmirror = False self.assertEqual(h.fastestmirror, False) self.assertEqual(h.lowspeedtime, 30) h.lowspeedtime = 20 self.assertEqual(h.lowspeedtime, 20) h.lowspeedtime = None self.assertEqual(h.lowspeedtime, 30) self.assertEqual(h.lowspeedlimit, 1000) h.lowspeedlimit = 55 self.assertEqual(h.lowspeedlimit, 55) h.lowspeedlimit = None self.assertEqual(h.lowspeedlimit, 1000) self.assertFalse(h.hmfcb) h.hmfcb = foo_hmfcb self.assertEqual(h.hmfcb, foo_hmfcb) h.hmfcb = None self.assertFalse(h.hmfcb) self.assertTrue(h.sslverifypeer) h.sslverifypeer = False self.assertEqual(h.sslverifypeer, False) h.sslverifypeer = None self.assertTrue(h.sslverifypeer) self.assertTrue(h.sslverifyhost) h.sslverifyhost = False self.assertEqual(h.sslverifyhost, False) h.sslverifyhost = None self.assertTrue(h.sslverifyhost) self.assertEqual(h.ipresolve, librepo.IPRESOLVE_WHATEVER) h.ipresolve = librepo.IPRESOLVE_V4 self.assertEqual(h.ipresolve, librepo.IPRESOLVE_V4) h.ipresolve = None self.assertEqual(h.ipresolve, librepo.IPRESOLVE_WHATEVER) self.assertEqual(h.allowedmirrorfailures, 4) h.allowedmirrorfailures = 1 self.assertEqual(h.allowedmirrorfailures, 1) h.allowedmirrorfailures = None self.assertEqual(h.allowedmirrorfailures, 4) self.assertEqual(h.adaptivemirrorsorting, 1) h.adaptivemirrorsorting = 0 self.assertEqual(h.adaptivemirrorsorting, 0) h.adaptivemirrorsorting = None self.assertEqual(h.adaptivemirrorsorting, 1) self.assertEqual(h.gnupghomedir, None) h.gnupghomedir = "/tmp/keyring" self.assertEqual(h.gnupghomedir, "/tmp/keyring") h.gnupghomedir = None self.assertEqual(h.gnupghomedir, None) h.gnupghomedir = "" self.assertEqual(h.gnupghomedir, "") self.assertEqual(h.fastestmirrortimeout, 2.0) h.fastestmirrortimeout = 3.14 self.assertEqual(h.fastestmirrortimeout, 3.14) h.fastestmirrortimeout = None self.assertEqual(h.fastestmirrortimeout, 2.0) self.assertEqual(h.httpheader, None) h.httpheader = ["Accept: text/xml", "charsets: utf-8"] self.assertEqual(h.httpheader, ["Accept: text/xml", "charsets: utf-8"]) h.httpheader = None self.assertEqual(h.httpheader, None) self.assertEqual(h.httpauthmethods, librepo.LR_AUTH_BASIC) h.httpauthmethods = librepo.LR_AUTH_NTLM self.assertEqual(h.httpauthmethods, librepo.LR_AUTH_NTLM) h.httpauthmethods = None self.assertEqual(h.httpauthmethods, librepo.LR_AUTH_BASIC) self.assertEqual(h.proxyauthmethods, librepo.LR_AUTH_BASIC) h.proxyauthmethods = librepo.LR_AUTH_NTLM self.assertEqual(h.proxyauthmethods, librepo.LR_AUTH_NTLM) h.proxyauthmethods = None self.assertEqual(h.proxyauthmethods, librepo.LR_AUTH_BASIC) self.assertTrue(h.ftpuseepsv) h.ftpuseepsv = 0 self.assertFalse(h.ftpuseepsv) h.ftpuseepsv = 1 self.assertTrue(h.ftpuseepsv) h.ftpuseepsv = False self.assertFalse(h.ftpuseepsv) h.ftpuseepsv = True self.assertTrue(h.ftpuseepsv) h.ftpuseepsv = 0 h.ftpuseepsv = None self.assertTrue(h.ftpuseepsv) def test_handle_setopt_none_value(self): """Using None in setopt.""" h = librepo.Handle() h.setopt(librepo.LRO_LOCAL, None) h.local = None h.setopt(librepo.LRO_HTTPAUTH, None) h.httpauth = None h.setopt(librepo.LRO_USERPWD, None) h.userpwd = None h.setopt(librepo.LRO_PROXY, None) h.proxy = None h.setopt(librepo.LRO_PROXYPORT, None) # None sets default value h.proxyport = None h.setopt(librepo.LRO_PROXYTYPE, None) h.proxytype = None h.setopt(librepo.LRO_PROXYAUTH, None) h.proxyauth = None h.setopt(librepo.LRO_PROXYUSERPWD, None) h.proxyuserpwd = None h.setopt(librepo.LRO_PROGRESSDATA, None) h.progressdata = None h.setopt(librepo.LRO_MAXSPEED, None) # None sets default value h.maxspeed = None h.setopt(librepo.LRO_CONNECTTIMEOUT, None) # None sets default value h.connecttimeout = None h.setopt(librepo.LRO_IGNOREMISSING, None) h.ignoremissing = None h.setopt(librepo.LRO_INTERRUPTIBLE, None) h.interruptible = None h.setopt(librepo.LRO_USERAGENT, None) h.useragent = None h.setopt(librepo.LRO_FETCHMIRRORS, None) h.fetchmirrors = None h.setopt(librepo.LRO_MAXMIRRORTRIES, None) h.maxmirrortries = None h.setopt(librepo.LRO_VARSUB, None) h.varsub = None h.setopt(librepo.LRO_FASTESTMIRROR, None) h.fastestmirror = None h.setopt(librepo.LRO_FASTESTMIRRORCACHE, None) h.fastestmirrorcache = None h.setopt(librepo.LRO_FASTESTMIRRORMAXAGE, None) h.fastestmirrormaxage = None h.setopt(librepo.LRO_FASTESTMIRRORDATA, None) h.fastestmirrordata = None h.setopt(librepo.LRO_LOWSPEEDTIME, None) h.lowspeedtime = None h.setopt(librepo.LRO_LOWSPEEDLIMIT, None) h.lowspeedlimit = None h.setopt(librepo.LRO_GPGCHECK, None) h.gpgcheck = None h.setopt(librepo.LRO_CHECKSUM, None) h.checksum = None h.setopt(librepo.LRO_LOWSPEEDTIME, None) h.lowspeedtime = None h.setopt(librepo.LRO_LOWSPEEDLIMIT, None) h.lowspeedlimit = None h.setopt(librepo.LRO_PROGRESSCB, None) h.progresscb = None h.setopt(librepo.LRO_PROGRESSCB, foo_cb) h.progresscb = foo_cb h.setopt(librepo.LRO_PROGRESSCB, None) h.progresscb = None def fmcallback(userdata, stage, data): pass h.setopt(librepo.LRO_FASTESTMIRRORCB, None) h.fastestmirrorcb = None h.setopt(librepo.LRO_FASTESTMIRRORCB, fmcallback) h.fastestmirrorcb = fmcallback h.setopt(librepo.LRO_FASTESTMIRRORCB, None) h.fastestmirror = None h.setopt(librepo.LRO_HMFCB, None) h.hmfcb = None h.setopt(librepo.LRO_HMFCB, foo_hmfcb) h.hmfcb = foo_hmfcb h.setopt(librepo.LRO_HMFCB, None) h.hmfcb = None h.setopt(librepo.LRO_SSLVERIFYPEER, None) h.sslverifypeer = None h.setopt(librepo.LRO_SSLVERIFYHOST, None) h.sslverifyhost = None h.setopt(librepo.LRO_SSLCLIENTCERT, None) h.sslclientcert = None h.setopt(librepo.LRO_SSLCLIENTKEY, None) h.sslclientkey = None h.setopt(librepo.LRO_SSLCACERT, None) h.sslcacert = None h.setopt(librepo.LRO_IPRESOLVE, None) h.ipresolve = None h.setopt(librepo.LRO_ALLOWEDMIRRORFAILURES, None) h.allowedmirrorfailures = None h.setopt(librepo.LRO_ADAPTIVEMIRRORSORTING, None) h.adaptivemirrorsorting = None h.setopt(librepo.LRO_GNUPGHOMEDIR, None) h.gnupghomedir = None h.setopt(librepo.LRO_FASTESTMIRRORTIMEOUT, None) h.fastestmirrortimeout = None h.setopt(librepo.LRO_HTTPHEADER, None) h.httpheader = None h.setopt(librepo.LRO_FTPUSEEPSV, None) h.ftpuseepsv = None h.httpauthmethods = None h.proxyauthmethods = None librepo-1.12.1/tests/python/tests/test_result.py000066400000000000000000000023311372027637700220210ustar00rootroot00000000000000import unittest import librepo from librepo import LibrepoException class TestCaseResult(unittest.TestCase): def test_result_getinfo(self): r = librepo.Result() self.assertTrue(r) self.assertRaises(ValueError, r.getinfo, 99999999) self.assertFalse(r.getinfo(librepo.LRR_YUM_REPO)) self.assertFalse(r.getinfo(librepo.LRR_YUM_REPOMD)) self.assertRaises(LibrepoException, r.getinfo, librepo.LRR_YUM_TIMESTAMP) self.assertFalse(r.getinfo(librepo.LRR_RPMMD_REPO)) self.assertFalse(r.getinfo(librepo.LRR_RPMMD_REPOMD)) self.assertRaises(LibrepoException, r.getinfo, librepo.LRR_RPMMD_TIMESTAMP) def test_result_attrs(self): r = librepo.Result() self.assertTrue(r) self.assertRaises(AttributeError, getattr, r, 'foobar_attr') # Attrs should not be filled (that's why None or # LibrepoException is expected), but they definitelly # should exists (not AttributeError should be raised) self.assertFalse(r.yum_repo) self.assertFalse(r.yum_repomd) self.assertRaises(LibrepoException, getattr, r, 'yum_timestamp') self.assertFalse(r.rpmmd_repo) self.assertFalse(r.rpmmd_repomd) librepo-1.12.1/tests/python/tests/test_sanity.py000066400000000000000000000033101372027637700220100ustar00rootroot00000000000000import unittest import librepo class TestCaseSanity(unittest.TestCase): def test_raw_handle_sanity(self): h = librepo.Handle() self.assertEqual(h.update, False) self.assertEqual(h.urls, []) self.assertEqual(h.mirrorlist, None) self.assertEqual(h.local, False) self.assertEqual(h.progresscb, None) self.assertEqual(h.progressdata, None) self.assertEqual(h.destdir, None) self.assertEqual(h.repotype, 0) self.assertEqual(h.useragent, None) self.assertEqual(h.yumdlist, None) self.assertEqual(h.rpmmddlist, None) self.assertEqual(h.yumblist, None) self.assertEqual(h.rpmmdblist, None) self.assertEqual(h.fetchmirrors, False) self.assertEqual(h.maxmirrortries, 0) self.assertEqual(h.varsub, None) self.assertEqual(h.mirrors, []) self.assertEqual(h.metalink, None) self.assertEqual(h.hmfcb, None) def test_raw_result_sanity(self): r = librepo.Result() self.assertEqual(r.yum_repo, None) self.assertEqual(r.yum_repomd, None) self.assertEqual(r.rpmmd_repo, None) self.assertEqual(r.rpmmd_repomd, None) def test_raw_result_sanity(self): t = librepo.PackageTarget("foo") self.assertEqual(t.handle, None) self.assertEqual(t.relative_url, "foo") self.assertEqual(t.dest, None) self.assertEqual(t.base_url, None) self.assertEqual(t.checksum_type, librepo.CHECKSUM_UNKNOWN) self.assertEqual(t.checksum, None) self.assertEqual(t.progresscb, None) self.assertEqual(t.cbdata, None) self.assertEqual(t.local_path, None) self.assertEqual(t.err, None) librepo-1.12.1/tests/python/tests/test_version.py000066400000000000000000000003211372027637700221650ustar00rootroot00000000000000import unittest import librepo class TestCaseVersion(unittest.TestCase): def test_version(self): self.assertTrue(len(librepo.VERSION) >= 5) self.assertEqual(librepo.VERSION.count('.'), 2) librepo-1.12.1/tests/python/tests/test_yum.py000066400000000000000000000013541372027637700213210ustar00rootroot00000000000000import unittest import os import time import librepo from tests.base import TestCase, TEST_DATA REPO_YUM_01_PATH = TEST_DATA+"/repo_yum_01/" class TestCaseYum(unittest.TestCase): def test_yum_repomd_get_age(self): h = librepo.Handle() h.urls = [REPO_YUM_01_PATH] h.repotype = librepo.LR_YUMREPO h.local = True r = h.perform() repomd_path = r.getinfo(librepo.LRR_YUM_REPO)["repomd"] mtime = os.stat(repomd_path).st_mtime calculated_age = time.time() - mtime librepo_age = librepo.yum_repomd_get_age(r) # Approximate comparison self.assertTrue(((calculated_age - 10) < librepo_age) or \ (calculated_age + 10) > librepo_age) librepo-1.12.1/tests/python/tests/test_yum_package_downloading.py000066400000000000000000001015301372027637700253560ustar00rootroot00000000000000import os import shutil import os.path import librepo import hashlib import unittest import tempfile import xattr import tests.servermock.yum_mock.config as config from tests.base import TestCaseWithFlask from tests.servermock.server import app class TestCaseYumPackageDownloading(TestCaseWithFlask): def setUp(self): self.tmpdir = tempfile.mkdtemp(prefix="librepotest-", dir="./") def tearDown(self): shutil.rmtree(self.tmpdir) def test_download_package_01(self): """Just test download. Destination dir is specified by LRO_DESTDIR in handle""" h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.download(config.PACKAGE_01_01) pkg = os.path.join(self.tmpdir, config.PACKAGE_01_01) self.assertTrue(os.path.isfile(pkg)) def test_download_package_02(self): """Download with checksum check. Destination file is specified by full path in dest option of download() method and checksum type is specified by string""" h = librepo.Handle() destination = os.path.join(self.tmpdir, "pkg.rpm") url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.checksum = True h.download(config.PACKAGE_01_01, dest=destination, checksum=config.PACKAGE_01_01_SHA256, checksum_type="sha256") self.assertTrue(os.path.isfile(destination)) def test_download_package_03(self): """Download with checksum check. Destination dir is not specified and cwd shoud be used""" cwd = os.getcwd() h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.checksum = True os.chdir(self.tmpdir) h.download(config.PACKAGE_01_01, checksum=config.PACKAGE_01_01_SHA256, checksum_type=librepo.CHECKSUM_SHA256) os.chdir(cwd) pkg = os.path.join(self.tmpdir, config.PACKAGE_01_01) self.assertTrue(os.path.isfile(pkg)) def test_download_package_04(self): """Download with checksum check. Destination dir is specified in dest option of download() method""" h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.checksum = True h.download(config.PACKAGE_01_01, dest=self.tmpdir+"/", checksum=config.PACKAGE_01_01_SHA256, checksum_type=librepo.CHECKSUM_SHA256) pkg = os.path.join(self.tmpdir, config.PACKAGE_01_01) self.assertTrue(os.path.isfile(pkg)) def test_download_package_via_metalink(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.METALINK_GOOD_01) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.download(config.PACKAGE_01_01, checksum=config.PACKAGE_01_01_SHA256, checksum_type=librepo.CHECKSUM_SHA256) pkg = os.path.join(self.tmpdir, config.PACKAGE_01_01) self.assertTrue(os.path.isfile(pkg)) def test_download_package_via_mirrorlist(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.MIRRORLIST_GOOD_01) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.download(config.PACKAGE_01_01, checksum=config.PACKAGE_01_01_SHA256, checksum_type=librepo.CHECKSUM_SHA256) pkg = os.path.join(self.tmpdir, config.PACKAGE_01_01) self.assertTrue(os.path.isfile(pkg)) def test_download_package_with_baseurl(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.BADURL) baseurl = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.download(config.PACKAGE_01_01, checksum=config.PACKAGE_01_01_SHA256, checksum_type=librepo.CHECKSUM_SHA256, base_url=baseurl) pkg = os.path.join(self.tmpdir, config.PACKAGE_01_01) self.assertTrue(os.path.isfile(pkg)) def test_download_package_with_baseurl_and_arguments(self): """ Tests that the path is correctly joined with a baseurl that has a http argument after a question mark. """ h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.BADURL) baseurl = "%s%s?hello=1" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.download(config.PACKAGE_01_01, checksum=config.PACKAGE_01_01_SHA256, checksum_type=librepo.CHECKSUM_SHA256, base_url=baseurl) pkg = os.path.join(self.tmpdir, config.PACKAGE_01_01) self.assertTrue(os.path.isfile(pkg)) class TestCaseYumPackagesDownloading(TestCaseWithFlask): application = app # @classmethod # def setUpClass(cls): # super(TestCaseYumPackageDownloading, cls).setUpClass() def setUp(self): self.tmpdir = tempfile.mkdtemp(prefix="librepotest-", dir="./") def tearDown(self): shutil.rmtree(self.tmpdir) def test_packagetarget_sanity_00(self): t = librepo.PackageTarget("foo") self.assertEqual(t.relative_url, "foo") self.assertEqual(t.dest, None) self.assertEqual(t.checksum_type, librepo.CHECKSUM_UNKNOWN) self.assertEqual(t.checksum, None) self.assertEqual(t.expectedsize, 0) self.assertEqual(t.base_url, None) self.assertEqual(t.resume, False) self.assertEqual(t.progresscb, None) self.assertEqual(t.cbdata, None) self.assertEqual(t.handle, None) self.assertEqual(t.endcb, None) self.assertEqual(t.mirrorfailurecb, None) def test_packagetarget_sanity_01(self): h = librepo.Handle() cbdata = {"a": "b"} def progresscb(a, b, c): return 0 def endcb(a): return def mirrorfailurecb(a, b): return 0; t = librepo.PackageTarget("foo", dest="bar", checksum_type=librepo.CHECKSUM_SHA512, checksum="xxx", expectedsize=123, base_url="basefoo", resume=True, progresscb=progresscb, cbdata=cbdata, handle=h, endcb=endcb, mirrorfailurecb=mirrorfailurecb) self.assertEqual(t.relative_url, "foo") self.assertEqual(t.dest, "bar") self.assertEqual(t.checksum_type, librepo.CHECKSUM_SHA512) self.assertEqual(t.checksum, "xxx") self.assertEqual(t.expectedsize, 123) self.assertEqual(t.base_url, "basefoo") self.assertEqual(t.resume, True) self.assertEqual(t.progresscb, progresscb) self.assertEqual(t.cbdata, cbdata) self.assertEqual(t.handle, h) self.assertEqual(t.endcb, endcb) self.assertEqual(t.mirrorfailurecb, mirrorfailurecb) def test_download_packages_00(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO pkgs = [] librepo.download_packages(pkgs) def test_download_packages_01(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir)) librepo.download_packages(pkgs) for pkg in pkgs: self.assertTrue(pkg.err is None) self.assertTrue(os.path.isfile(pkg.local_path)) def test_download_packages_02(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir)) dest = os.path.join(self.tmpdir, "foo-haha-lol.rpm") pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=dest)) librepo.download_packages(pkgs) for pkg in pkgs: self.assertTrue(pkg.err is None) self.assertTrue(os.path.isfile(pkg.local_path)) self.assertTrue(os.path.isfile(dest)) def test_download_packages_02_with_failfast(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir)) dest = os.path.join(self.tmpdir, "foo-haha-lol.rpm") pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=dest)) librepo.download_packages(pkgs, failfast=True) for pkg in pkgs: self.assertTrue(pkg.err is None) self.assertTrue(os.path.isfile(pkg.local_path)) self.assertTrue(os.path.isfile(dest)) def test_download_packages_one_url_is_bad_01(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir)) pkgs.append(librepo.PackageTarget("so_bad_url_of_foo_rpm.rpm", handle=h, dest=self.tmpdir)) librepo.download_packages(pkgs) self.assertTrue(pkgs[0].err is None) self.assertTrue(os.path.isfile(pkgs[0].local_path)) self.assertTrue(pkgs[1].err is not None) self.assertFalse(os.path.isfile(pkgs[1].local_path)) def test_download_packages_one_url_is_bad_with_failfast(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir)) pkgs.append(librepo.PackageTarget("so_bad_url_of_foo_rpm.rpm", handle=h, dest=self.tmpdir)) self.assertRaises(librepo.LibrepoException, librepo.download_packages, pkgs, failfast=True) # Err state of first download is undefined, # it could be error because of interruption # or could be None (when the download was successful) # in case that it was downloaded at one shot before # the second download fails. # XXX: TODO #self.assertTrue(os.path.isfile(pkgs[0].local_path)) self.assertTrue(pkgs[1].err is not None) self.assertFalse(os.path.isfile(pkgs[1].local_path)) def test_download_packages_with_checksum_check(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir, checksum_type=librepo.SHA256, checksum=config.PACKAGE_01_01_SHA256)) librepo.download_packages(pkgs) pkg = pkgs[0] self.assertEqual(pkg.relative_url, config.PACKAGE_01_01) self.assertEqual(pkg.dest, self.tmpdir) self.assertEqual(pkg.base_url, None) self.assertEqual(pkg.checksum_type, librepo.SHA256) self.assertEqual(pkg.checksum, config.PACKAGE_01_01_SHA256) self.assertEqual(pkg.resume, 0) self.assertTrue(os.path.isfile(pkg.local_path)) self.assertEqual(pkg.local_path, os.path.join(self.tmpdir, os.path.basename(config.PACKAGE_01_01))) self.assertTrue(pkg.err is None) def test_download_packages_with_bad_checksum(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir, checksum_type=librepo.SHA256, checksum="badchecksum")) librepo.download_packages(pkgs) pkg = pkgs[0] self.assertTrue(pkg.err) def test_download_packages_with_bad_checksum_with_failfast(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir, checksum_type=librepo.SHA256, checksum="badchecksum")) self.assertRaises(librepo.LibrepoException, librepo.download_packages, pkgs, failfast=True) pkg = pkgs[0] self.assertTrue(pkg.err) def test_download_packages_with_baseurl(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = ["."] h.repotype = librepo.LR_YUMREPO pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir, base_url=url)) librepo.download_packages(pkgs) pkg = pkgs[0] self.assertTrue(pkg.err is None) self.assertTrue(os.path.isfile(pkg.local_path)) def test_download_packages_with_resume(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir, resume=False)) librepo.download_packages(pkgs) pkg = pkgs[0] self.assertTrue(pkg.err is None) self.assertTrue(os.path.isfile(pkg.local_path)) # Again with resume on # Because the package already exists and checksum matches, # then download should not be performed! pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir, resume=True, checksum_type=librepo.SHA256, checksum=config.PACKAGE_01_01_SHA256)) librepo.download_packages(pkgs) pkg = pkgs[0] self.assertEqual(pkg.err, "Already downloaded") self.assertTrue(os.path.isfile(pkg.local_path)) ## Failfast ignore this type of error (Already downloaded error) librepo.download_packages(pkgs, failfast=True) pkg = pkgs[0] self.assertEqual(pkg.err, "Already downloaded") self.assertTrue(os.path.isfile(pkg.local_path)) def test_download_packages_with_callback(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO cbdata = {'called': 0} def cb(cbdata, total, downloaded): cbdata["called"] += 1 cbdata["total"] = total cbdata["downloaded"] = downloaded pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir, progresscb=cb, cbdata=cbdata)) librepo.download_packages(pkgs) pkg = pkgs[0] self.assertTrue(pkg.err is None) self.assertTrue(pkg.progresscb == cb) self.assertTrue(pkg.cbdata == cbdata) self.assertTrue(os.path.isfile(pkg.local_path)) self.assertTrue(cbdata["called"] > 0) self.assertTrue(cbdata["downloaded"] > 0) self.assertTrue(cbdata["total"] > 0) self.assertEqual(cbdata["downloaded"], cbdata["total"]) def test_download_packages_without_handle(self): complete_url = "%s%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH, config.PACKAGE_01_01) pkgs = [] pkgs.append(librepo.PackageTarget(complete_url, dest=self.tmpdir)) librepo.download_packages(pkgs) pkg = pkgs[0] self.assertTrue(pkg.err is None) self.assertTrue(os.path.isfile(pkg.local_path)) def test_download_packages_from_different_repos(self): h1 = librepo.Handle() h2 = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h1.urls = [url] h1.repotype = librepo.LR_YUMREPO url = "%s%s" % (self.MOCKURL, config.REPO_YUM_03_PATH) h2.urls = [url] h2.repotype = librepo.LR_YUMREPO pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h1, dest=self.tmpdir)) pkgs.append(librepo.PackageTarget(config.PACKAGE_03_01, handle=h2, dest=self.tmpdir)) librepo.download_packages(pkgs) pkg = pkgs[0] self.assertTrue(pkg.handle == h1) self.assertTrue(pkg.err is None) self.assertTrue(os.path.isfile(pkg.local_path)) pkg = pkgs[1] self.assertTrue(pkg.handle == h2) self.assertTrue(pkg.err is None) self.assertTrue(os.path.isfile(pkg.local_path)) def test_download_packages_with_expectedsize(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO expectedsize = 1057084 pkgs = [] pkgs.append(h.new_packagetarget(config.PACKAGE_01_01, dest=self.tmpdir, expectedsize=expectedsize)) librepo.download_packages(pkgs) pkg = pkgs[0] self.assertEqual(pkg.expectedsize, expectedsize) self.assertTrue(pkg.err is None) self.assertTrue(os.path.isfile(pkg.local_path)) def test_download_packages_with_fastestmirror_enabled_1(self): h = librepo.Handle() cbdata = { "called": False, } def fastestmirrorstatuscallback(userdata, stage, data): cbdata["called"] = True url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.fastestmirror = True h.fastestmirrorcb = fastestmirrorstatuscallback h.download(config.PACKAGE_01_01) pkgs = [] pkgs.append(h.new_packagetarget(config.PACKAGE_01_01, dest=self.tmpdir)) librepo.download_packages(pkgs) pkg = pkgs[0] self.assertTrue(pkg.err is None) self.assertTrue(os.path.isfile(pkg.local_path)) # There is only one mirror, fastestmirror # detection should be skiped self.assertFalse(cbdata["called"]) def test_download_packages_with_fastestmirror_enabled_2(self): h = librepo.Handle() cbdata = { "called": False, "detection": False, } def fastestmirrorstatuscallback(cbdata, stage, data): cbdata["called"] = True if stage == librepo.FMSTAGE_DETECTION: cbdata["detection"] = True url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url, "http://foobarblabla"] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.fastestmirror = True h.fastestmirrordata = cbdata h.fastestmirrorcb = fastestmirrorstatuscallback h.download(config.PACKAGE_01_01) pkgs = [] pkgs.append(h.new_packagetarget(config.PACKAGE_01_01, dest=self.tmpdir)) librepo.download_packages(pkgs) pkg = pkgs[0] self.assertTrue(pkg.err is None) self.assertTrue(os.path.isfile(pkg.local_path)) # There is only one mirror, fastestmirror # detection should be skiped self.assertTrue(cbdata["called"]) self.assertTrue(cbdata["detection"]) def test_download_packages_from_different_mirrors_1(self): cbdata = { "failed_urls" : [], } def mirrorfailurecb(userdata, msg, url): cbdata["failed_urls"].append(url) return None h = librepo.Handle() url1 = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) url2 = "%s%s" % (self.MOCKURL, config.REPO_YUM_03_PATH) url3 = "%s%s" % (self.MOCKURL, config.REPO_YUM_04_PATH) h.mirrorlist = "%s%s" % (self.MOCKURL, config.METALINK_MIRRORS_01) h.repotype = librepo.LR_YUMREPO h.maxparalleldownloads = 1 h.fastestmirror = True pkgs = [] for P in [config.PACKAGE_01_01, config.PACKAGE_03_01, config.PACKAGE_04_01]: pkgs.append(librepo.PackageTarget(P, handle=h, dest=self.tmpdir, mirrorfailurecb=mirrorfailurecb)) librepo.download_packages(pkgs, failfast=True) # the mirror list should be 1, 3, 4. The metalink file # defines preference order. This is the order in which # the URLs are listed in the file self.assertEquals(h.mirrors, [url1, url2, url3]) # YUM 01 contains P_01 # YUM 03 contains P_03 # YUM_04 contains P_04 # Mirror order of preference is 01 03, 04 # When a package fails to download, librepo moves to the next mirror; # after a successfull download of the package librepo should continue trying # to download the remaining packages from the first mirror. e.g. # always try to download from the fastest mirror containing the package # Expected download sequence (restart from fastest mirror): # - P_01 from YUM_01 - PASS # - P_03 from YUM_01 - FAIL # - P_03 from YUM_03 - PASS # - P_04 from YUM_04 - FAIL # - P_04 from YUM_03 - FAIL # - P_04 from YUM_04 - PASS self.assertEquals(len(cbdata["failed_urls"]), 3) self.assertEquals(cbdata["failed_urls"][0], url1+config.PACKAGE_03_01) self.assertEquals(cbdata["failed_urls"][1], url1+config.PACKAGE_04_01) self.assertEquals(cbdata["failed_urls"][2], url2+config.PACKAGE_04_01) # assert that all packages have been downloaded for pkg in pkgs: self.assertTrue(pkg.err is None) self.assertTrue(os.path.isfile(pkg.local_path)) def test_download_packages_with_bad_first_mirror_1(self): h = librepo.Handle() url1 = "%s%s" % (self.MOCKURL, config.REPO_YUM_02_PATH) url2 = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) url3 = "%s%s" % (self.MOCKURL, config.REPO_YUM_03_PATH) h.urls = [url1, url2, url3] h.repotype = librepo.LR_YUMREPO h.maxparalleldownloads = 1 h.allowedmirrorfailures = 1 pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_03_01, handle=h, dest=self.tmpdir)) pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir)) # Should raise an error, because, the first two mirror # should be disabled during download of the first package # and when is the time to download the second package, # the repo that contains the package is disabled yet. self.assertRaises(librepo.LibrepoException, librepo.download_packages, pkgs, failfast=True) self.assertTrue(pkgs[0].err is None) self.assertTrue(os.path.isfile(pkgs[0].local_path)) self.assertTrue(pkgs[1].err is not None) self.assertFalse(os.path.isfile(pkgs[1].local_path)) def test_download_packages_with_resume_02(self): # If download that should be resumed fails, # the original file should not be modified or deleted h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.allowedmirrorfailures = 1 fn = os.path.join(self.tmpdir, "package.rpm") # Make fake unfinished download CONTENT = "0123456789" open(fn, "w").write(CONTENT) # Mark the file as it was downloaded by Librepo # Otherwise librepo refuse to resume try: xattr.setxattr(fn, "user.Librepo.DownloadInProgress".encode("utf-8"), "".encode("utf-8")) except IOError as err: if err.errno == 95: self.skipTest('extended attributes are not supported') raise # Now try to resume from bad URL pkgs = [] pkgs.append(librepo.PackageTarget("bad_path.rpm", handle=h, dest=fn, resume=True, checksum_type=librepo.SHA256, checksum=config.PACKAGE_01_01_SHA256)) # Download should fail (path is bad, the file doesn't exist) self.assertRaises(librepo.LibrepoException, librepo.download_packages, pkgs, failfast=True) # The package should exists (should not be removed or changed) pkg = pkgs[0] self.assertTrue(pkg.err) self.assertTrue(os.path.isfile(pkg.local_path)) self.assertEqual(open(pkg.local_path, "rb").read(), CONTENT.encode('utf-8')) def test_download_packages_mirror_penalization_01(self): # This test is useful for mirror penalization testing # with debug output on: # LIBREPO_DEBUG_ADAPTIVEMIRRORSORTING="1" # # You can use it to check adaptive mirror sorting behavior # # E.g. from librepo checkout dir: # LIBREPO_DEBUG_ADAPTIVEMIRRORSORTING="1" LIBREPO_DEBUG="1" PYTHONPATH=`readlink -f ./build/librepo/python/python2/` nosetests -s -v tests/python/tests/test_yum_package_downloading.py:TestCaseYumPackagesDownloading.test_download_packages_mirror_penalization_01 h = librepo.Handle() url1 = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) url2 = "%s%s" % (self.MOCKURL, config.REPO_YUM_03_PATH) url3 = "%s%s" % (self.MOCKURL, config.REPO_YUM_04_PATH) h.urls = [url1, url2, url3] h.repotype = librepo.LR_YUMREPO h.maxparalleldownloads = 1 h.allowedmirrorfailures = -1 h.adaptivemirrorsorting = True pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_03_01, handle=h, dest=self.tmpdir)) pkgs.append(librepo.PackageTarget(config.PACKAGE_03_01, handle=h, dest=self.tmpdir)) pkgs.append(librepo.PackageTarget(config.PACKAGE_04_01, handle=h, dest=self.tmpdir)) pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir)) pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir)) pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir)) pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir)) librepo.download_packages(pkgs, failfast=False) for pkg in pkgs: self.assertTrue(pkg.err is None) self.assertTrue(os.path.isfile(pkg.local_path)) def test_download_packages_with_offline_enabled_01(self): url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h = librepo.Handle() h.urls = [url] h.repotype = librepo.YUMREPO h.offline = True pkgs = [] pkgs.append(librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=self.tmpdir)) librepo.download_packages(pkgs) # Offline is True, no package should be downloaded successfully for pkg in pkgs: self.assertTrue(pkg.err) h.offline = False librepo.download_packages(pkgs) # Offline is False, the package should be downloaded successfully for pkg in pkgs: self.assertFalse(pkg.err) def test_download_packages_with_offline_enabled_02(self): url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h = librepo.Handle() h.repotype = librepo.YUMREPO h.offline = True h.urls = [url] # 1) Try to download a package with only rel path specified # (mirrors from handle will be used) pkgs = [librepo.PackageTarget(config.PACKAGE_01_01, handle=h, dest=os.path.join(self.tmpdir, "01"))] self.assertRaises(librepo.LibrepoException, librepo.download_packages, pkgs, failfast=True) self.assertTrue(pkgs[0].err) # Remove mirrors from handle (the one which was inserted via urls opt) h.urls = [] # 2) Try to download a package with full path specified full_url = os.path.join(url, config.PACKAGE_01_01) pkgs = [librepo.PackageTarget(full_url, handle=h, dest=os.path.join(self.tmpdir, "01"))] self.assertRaises(librepo.LibrepoException, librepo.download_packages, pkgs, failfast=True) self.assertTrue(pkgs[0].err) # 3) Try to download a package with rel path and base url specified pkgs = [librepo.PackageTarget(config.PACKAGE_01_01, base_url=url, handle=h, dest=os.path.join(self.tmpdir, "01"))] self.assertRaises(librepo.LibrepoException, librepo.download_packages, pkgs, failfast=True) self.assertTrue(pkgs[0].err) librepo-1.12.1/tests/python/tests/test_yum_repo_downloading.py000066400000000000000000002135521372027637700247400ustar00rootroot00000000000000import sys import time import shutil import os.path import tempfile import unittest import librepo from tests.base import Context, TestCaseWithFlask, TEST_DATA from tests.servermock.server import app import tests.servermock.yum_mock.config as config PUB_KEY = TEST_DATA+"/key.pub" class TestCaseYumRepoDownloading(TestCaseWithFlask): def setUp(self): self.tmpdir = tempfile.mkdtemp(prefix="librepotest-") # Import public key into the temporary gpg keyring self._gnupghome = os.environ.get('GNUPGHOME') gpghome = os.path.join(self.tmpdir, "keyring") os.mkdir(gpghome, 0o700) os.environ['GNUPGHOME'] = gpghome self.ctx = Context() self.ctx.op_import(open(PUB_KEY, 'rb')) def tearDown(self): if self._gnupghome is None: os.environ.pop('GNUPGHOME') else: os.environ['GNUPGHOME'] = self._gnupghome shutil.rmtree(self.tmpdir, True) def test_download_repo_01(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) timestamp = r.getinfo(librepo.LRR_YUM_TIMESTAMP) self.assertEqual(yum_repo, { #'deltainfo': None, 'destdir': self.tmpdir, 'filelists': self.tmpdir+'/repodata/aeca08fccd3c1ab831e1df1a62711a44ba1922c9-filelists.xml.gz', 'filelists_db': self.tmpdir+'/repodata/4034dcea76c94d3f7a9616779539a4ea8cac288f-filelists.sqlite.bz2', #'group': None, #'group_gz': None, #'origin': None, 'other': self.tmpdir+'/repodata/a8977cdaa0b14321d9acfab81ce8a85e869eee32-other.xml.gz', 'other_db': self.tmpdir+'/repodata/fd96942c919628895187778633001cff61e872b8-other.sqlite.bz2', #'prestodelta': None, 'primary': self.tmpdir+'/repodata/4543ad62e4d86337cd1949346f9aec976b847b58-primary.xml.gz', 'primary_db': self.tmpdir+'/repodata/735cd6294df08bdf28e2ba113915ca05a151118e-primary.sqlite.bz2', 'repomd': self.tmpdir+'/repodata/repomd.xml', #'updateinfo': None, 'url': url, 'signature': None, 'mirrorlist': None, 'metalink': None} ) self.maxDiff = None self.assertEqual(yum_repomd, { 'content_tags': [], #'deltainfo': None, 'distro_tags': [], 'filelists': { 'checksum': 'aeca08fccd3c1ab831e1df1a62711a44ba1922c9', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '52d30ae3162ca863c63c345ffdb7f0e10c1414a5', 'checksum_open_type': 'sha1', 'checksum_type': 'sha1', 'db_version': 0, 'location_href': 'repodata/aeca08fccd3c1ab831e1df1a62711a44ba1922c9-filelists.xml.gz', 'size': 43310, 'size_open': 735088, 'timestamp': 1347459930}, 'filelists_db': { 'checksum': '4034dcea76c94d3f7a9616779539a4ea8cac288f', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '949c6b7b605b2bc66852630c841a5003603ca5b2', 'checksum_open_type': 'sha1', 'checksum_type': 'sha1', 'db_version': 10, 'location_href': 'repodata/4034dcea76c94d3f7a9616779539a4ea8cac288f-filelists.sqlite.bz2', 'size': 22575, 'size_open': 201728, 'timestamp': 1347459931}, #'group': None, #'group_gz': None, #'origin': None, 'other': { 'checksum': 'a8977cdaa0b14321d9acfab81ce8a85e869eee32', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '4b5b8874fb233a626b03b3260a1aa08dce90e81a', 'checksum_open_type': 'sha1', 'checksum_type': 'sha1', 'db_version': 0, 'location_href': 'repodata/a8977cdaa0b14321d9acfab81ce8a85e869eee32-other.xml.gz', 'size': 807, 'size_open': 1910, 'timestamp': 1347459930}, 'other_db': { 'checksum': 'fd96942c919628895187778633001cff61e872b8', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': 'c5262f62b6b3360722b9b2fb5d0a9335d0a51112', 'checksum_open_type': 'sha1', 'checksum_type': 'sha1', 'db_version': 10, 'location_href': 'repodata/fd96942c919628895187778633001cff61e872b8-other.sqlite.bz2', 'size': 1407, 'size_open': 8192, 'timestamp': 1347459931}, #'prestodelta': None, 'primary': { 'checksum': '4543ad62e4d86337cd1949346f9aec976b847b58', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '68457ceb8e20bda004d46e0a4dfa4a69ce71db48', 'checksum_open_type': 'sha1', 'checksum_type': 'sha1', 'db_version': 0, 'location_href': 'repodata/4543ad62e4d86337cd1949346f9aec976b847b58-primary.xml.gz', 'size': 936, 'size_open': 3385, 'timestamp': 1347459930}, 'primary_db': { 'checksum': '735cd6294df08bdf28e2ba113915ca05a151118e', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': 'ba636386312e1b597fc4feb182d04c059b2a77d5', 'checksum_open_type': 'sha1', 'checksum_type': 'sha1', 'db_version': 10, 'location_href': 'repodata/735cd6294df08bdf28e2ba113915ca05a151118e-primary.sqlite.bz2', 'size': 2603, 'size_open': 23552, 'timestamp': 1347459931}, 'repo_tags': [], 'revision': '1347459931', #'updateinfo': None } ) self.assertEqual(timestamp, 1347459931) # Test if all mentioned files really exist self.assertTrue(os.path.isdir(yum_repo["destdir"])) for key in yum_repo: if yum_repo[key] and (key not in ("url", "destdir")): self.assertTrue(os.path.isfile(yum_repo[key])) self.assertFalse(h.mirrors) self.assertFalse(h.metalink) def test_download_repo_01_rpmmd(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_RPMMDREPO h.destdir = self.tmpdir h.perform(r) rpmmd_repo = r.getinfo(librepo.LRR_RPMMD_REPO) rpmmd_repomd = r.getinfo(librepo.LRR_RPMMD_REPOMD) timestamp = r.getinfo(librepo.LRR_RPMMD_TIMESTAMP) self.assertEqual(rpmmd_repo, { 'destdir': self.tmpdir, 'url': url, 'signature': None, 'mirrorlist': None, 'metalink': None, 'repomd': self.tmpdir+'/repodata/repomd.xml', 'paths': { 'primary': self.tmpdir+'/repodata/4543ad62e4d86337cd1949346f9aec976b847b58-primary.xml.gz', 'primary_db': self.tmpdir+'/repodata/735cd6294df08bdf28e2ba113915ca05a151118e-primary.sqlite.bz2', 'filelists': self.tmpdir+'/repodata/aeca08fccd3c1ab831e1df1a62711a44ba1922c9-filelists.xml.gz', 'filelists_db': self.tmpdir+'/repodata/4034dcea76c94d3f7a9616779539a4ea8cac288f-filelists.sqlite.bz2', 'other': self.tmpdir+'/repodata/a8977cdaa0b14321d9acfab81ce8a85e869eee32-other.xml.gz', 'other_db': self.tmpdir+'/repodata/fd96942c919628895187778633001cff61e872b8-other.sqlite.bz2', } } ) self.assertEqual(rpmmd_repomd, { 'revision': '1347459931', 'content_tags': [], 'distro_tags': [], 'repo_tags': [], 'records': { 'primary': { 'checksum': '4543ad62e4d86337cd1949346f9aec976b847b58', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '68457ceb8e20bda004d46e0a4dfa4a69ce71db48', 'checksum_open_type': 'sha1', 'checksum_type': 'sha1', 'db_version': 0, 'location_href': 'repodata/4543ad62e4d86337cd1949346f9aec976b847b58-primary.xml.gz', 'size': 936, 'size_open': 3385, 'timestamp': 1347459930}, 'primary_db': { 'checksum': '735cd6294df08bdf28e2ba113915ca05a151118e', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': 'ba636386312e1b597fc4feb182d04c059b2a77d5', 'checksum_open_type': 'sha1', 'checksum_type': 'sha1', 'db_version': 10, 'location_href': 'repodata/735cd6294df08bdf28e2ba113915ca05a151118e-primary.sqlite.bz2', 'size': 2603, 'size_open': 23552, 'timestamp': 1347459931}, 'filelists': { 'checksum': 'aeca08fccd3c1ab831e1df1a62711a44ba1922c9', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '52d30ae3162ca863c63c345ffdb7f0e10c1414a5', 'checksum_open_type': 'sha1', 'checksum_type': 'sha1', 'db_version': 0, 'location_href': 'repodata/aeca08fccd3c1ab831e1df1a62711a44ba1922c9-filelists.xml.gz', 'size': 43310, 'size_open': 735088, 'timestamp': 1347459930}, 'filelists_db': { 'checksum': '4034dcea76c94d3f7a9616779539a4ea8cac288f', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '949c6b7b605b2bc66852630c841a5003603ca5b2', 'checksum_open_type': 'sha1', 'checksum_type': 'sha1', 'db_version': 10, 'location_href': 'repodata/4034dcea76c94d3f7a9616779539a4ea8cac288f-filelists.sqlite.bz2', 'size': 22575, 'size_open': 201728, 'timestamp': 1347459931}, 'other': { 'checksum': 'a8977cdaa0b14321d9acfab81ce8a85e869eee32', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '4b5b8874fb233a626b03b3260a1aa08dce90e81a', 'checksum_open_type': 'sha1', 'checksum_type': 'sha1', 'db_version': 0, 'location_href': 'repodata/a8977cdaa0b14321d9acfab81ce8a85e869eee32-other.xml.gz', 'size': 807, 'size_open': 1910, 'timestamp': 1347459930}, 'other_db': { 'checksum': 'fd96942c919628895187778633001cff61e872b8', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': 'c5262f62b6b3360722b9b2fb5d0a9335d0a51112', 'checksum_open_type': 'sha1', 'checksum_type': 'sha1', 'db_version': 10, 'location_href': 'repodata/fd96942c919628895187778633001cff61e872b8-other.sqlite.bz2', 'size': 1407, 'size_open': 8192, 'timestamp': 1347459931}, } } ) self.assertEqual(timestamp, 1347459931) # Test if all mentioned files really exist self.assertTrue(os.path.isdir(rpmmd_repo["destdir"])) for key in rpmmd_repo.get("records", []): self.assertTrue(os.path.isfile(rpmmd_repo[key])) self.assertFalse(h.mirrors) self.assertFalse(h.metalink) def test_download_repo_02(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_02_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertEqual(yum_repo, {'deltainfo': self.tmpdir+'/repodata/32d3307b672abf7356061912fa3dc9b54071c03a75c671111c1c8daf5ed1eb7e-deltainfo.xml.gz', 'destdir': self.tmpdir, 'filelists': self.tmpdir+'/repodata/2431efa18b5de6bfddb87da2a526362108226752d46ef3a298cd4bf39ba16b1d-filelists.xml.gz', 'filelists_db': self.tmpdir+'/repodata/5b37f89f9f4474801ec5f23dc30d3d6cf9cf663cb75a6656aaa864a041836ffe-filelists.sqlite.bz2', 'group': self.tmpdir+'/repodata/5b3b362d644e8fa3b359db57be0ff5de8a08365ce9a59cddc3205244a968231e-comps.xml', 'group_gz': self.tmpdir+'/repodata/c395ae7d8a9117f4e81aa23e37fb9da9865b50917f5f701b50d422875bb0cb14-comps.xml.gz', 'origin': self.tmpdir+'/repodata/c949d2b2371fab1a03d03b41057004caf1133a56e4c9236f63b3163ad358c941-pkgorigins.gz', 'other': self.tmpdir+'/repodata/76b2cfb04531a66e382f187e6a7c90905940d2b2f315b7fd738b839887d83c35-other.xml.gz', 'other_db': self.tmpdir+'/repodata/705a58b0e169bf1d2ade8e4aacc515086644ce16cee971906f920c798c5b17d0-other.sqlite.bz2', 'prestodelta': self.tmpdir+'/repodata/26e351e1a38eb1524574e86ab130ea4db780aa1a4a8bb741d37595ed203f931c-prestodelta.xml.gz', 'primary': self.tmpdir+'/repodata/5a8e6bbb940b151103b3970a26e32b8965da9e90a798b1b80ee4325308149d8d-primary.xml.gz', 'primary_db': self.tmpdir+'/repodata/a09c42730c03b0d5defa3fd9213794c49e9bafbc67acdd8d4e87a2adf30b8752-primary.sqlite.bz2', 'repomd': self.tmpdir+'/repodata/repomd.xml', 'updateinfo': self.tmpdir+'/repodata/65c4f66e2808d328890505c3c2f13bb35a96f457d1c21a6346191c4dc07e6080-updateinfo.xml.gz', 'url': url, 'signature': None, 'mirrorlist': None, 'metalink': None} ) self.assertEqual(yum_repomd, {'content_tags': ['binary-i386'], 'deltainfo': {'checksum': '32d3307b672abf7356061912fa3dc9b54071c03a75c671111c1c8daf5ed1eb7e', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '8a35a38aef926fd88f479f03a9a22e1ab7aa8bd1aeaa9d05cd696f101eee2846', 'checksum_open_type': 'sha256', 'checksum_type': 'sha256', 'db_version': 0, 'location_href': 'repodata/32d3307b672abf7356061912fa3dc9b54071c03a75c671111c1c8daf5ed1eb7e-deltainfo.xml.gz', 'size': 300, 'size_open': 492, 'timestamp': 1355335029}, 'distro_tags': [('cpe:/o:fedoraproject:fedora:17', 'r')], 'filelists': {'checksum': '2431efa18b5de6bfddb87da2a526362108226752d46ef3a298cd4bf39ba16b1d', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': 'afa4a01d7a692ab8105a39fed5535b5011f0c68de0efbc98f9d6ffea36de85fe', 'checksum_open_type': 'sha256', 'checksum_type': 'sha256', 'db_version': 0, 'location_href': 'repodata/2431efa18b5de6bfddb87da2a526362108226752d46ef3a298cd4bf39ba16b1d-filelists.xml.gz', 'size': 43338, 'size_open': 735112, 'timestamp': 1355393567}, 'filelists_db': {'checksum': '5b37f89f9f4474801ec5f23dc30d3d6cf9cf663cb75a6656aaa864a041836ffe', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '8239ecd9334a3bc4dfa9a242f7c4d545b08451a1ad468458e20f3d3f768652c3', 'checksum_open_type': 'sha256', 'checksum_type': 'sha256', 'db_version': 10, 'location_href': 'repodata/5b37f89f9f4474801ec5f23dc30d3d6cf9cf663cb75a6656aaa864a041836ffe-filelists.sqlite.bz2', 'size': 23038, 'size_open': 200704, 'timestamp': 1355393568}, 'group': {'checksum': '5b3b362d644e8fa3b359db57be0ff5de8a08365ce9a59cddc3205244a968231e', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': None, 'checksum_open_type': None, 'checksum_type': 'sha256', 'db_version': 0, 'location_href': 'repodata/5b3b362d644e8fa3b359db57be0ff5de8a08365ce9a59cddc3205244a968231e-comps.xml', 'size': 679, 'size_open': 0, 'timestamp': 1355393567}, 'group_gz': {'checksum': 'c395ae7d8a9117f4e81aa23e37fb9da9865b50917f5f701b50d422875bb0cb14', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '5b3b362d644e8fa3b359db57be0ff5de8a08365ce9a59cddc3205244a968231e', 'checksum_open_type': 'sha256', 'checksum_type': 'sha256', 'db_version': 0, 'location_href': 'repodata/c395ae7d8a9117f4e81aa23e37fb9da9865b50917f5f701b50d422875bb0cb14-comps.xml.gz', 'size': 331, 'size_open': 679, 'timestamp': 1355393567}, 'origin': {'checksum': 'c949d2b2371fab1a03d03b41057004caf1133a56e4c9236f63b3163ad358c941', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '3928c6aadcfdff101f4482db68c0d07f5777b1c7ad8424e41358bc5e87b8465b', 'checksum_open_type': 'sha256', 'checksum_type': 'sha256', 'db_version': 0, 'location_href': 'repodata/c949d2b2371fab1a03d03b41057004caf1133a56e4c9236f63b3163ad358c941-pkgorigins.gz', 'size': 140, 'size_open': 364, 'timestamp': 1355315696}, 'other': {'checksum': '76b2cfb04531a66e382f187e6a7c90905940d2b2f315b7fd738b839887d83c35', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '2169e09e2c6c91393d38866c501a8697d0a1d698dd3b1027969dc16d291d8915', 'checksum_open_type': 'sha256', 'checksum_type': 'sha256', 'db_version': 0, 'location_href': 'repodata/76b2cfb04531a66e382f187e6a7c90905940d2b2f315b7fd738b839887d83c35-other.xml.gz', 'size': 826, 'size_open': 1934, 'timestamp': 1355393567}, 'other_db': {'checksum': '705a58b0e169bf1d2ade8e4aacc515086644ce16cee971906f920c798c5b17d0', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '916ca5e879387dc1da51b57266bda28a2569d1773ca6c8ea80abe99d9adb373e', 'checksum_open_type': 'sha256', 'checksum_type': 'sha256', 'db_version': 10, 'location_href': 'repodata/705a58b0e169bf1d2ade8e4aacc515086644ce16cee971906f920c798c5b17d0-other.sqlite.bz2', 'size': 1462, 'size_open': 8192, 'timestamp': 1355393568}, 'prestodelta': {'checksum': '26e351e1a38eb1524574e86ab130ea4db780aa1a4a8bb741d37595ed203f931c', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '0052b222add25fed094793c24e73aa07fd598f43f73c1643de26c5e81f6d8c07', 'checksum_open_type': 'sha256', 'checksum_type': 'sha256', 'db_version': 0, 'location_href': 'repodata/26e351e1a38eb1524574e86ab130ea4db780aa1a4a8bb741d37595ed203f931c-prestodelta.xml.gz', 'size': 336, 'size_open': 574, 'timestamp': 1337937059}, 'primary': {'checksum': '5a8e6bbb940b151103b3970a26e32b8965da9e90a798b1b80ee4325308149d8d', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': 'b8d60e74c38b94f255c08c3fe5e10c166dcb52f2c4bfec6cae097a68fdd75e74', 'checksum_open_type': 'sha256', 'checksum_type': 'sha256', 'db_version': 0, 'location_href': 'repodata/5a8e6bbb940b151103b3970a26e32b8965da9e90a798b1b80ee4325308149d8d-primary.xml.gz', 'size': 956, 'size_open': 3411, 'timestamp': 1355393567}, 'primary_db': {'checksum': 'a09c42730c03b0d5defa3fd9213794c49e9bafbc67acdd8d4e87a2adf30b8752', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': '27b2200efa2c518e5dd5a59deb9ab33c2abca74cb74f5241e612b15931dcec37', 'checksum_open_type': 'sha256', 'checksum_type': 'sha256', 'db_version': 10, 'location_href': 'repodata/a09c42730c03b0d5defa3fd9213794c49e9bafbc67acdd8d4e87a2adf30b8752-primary.sqlite.bz2', 'size': 2649, 'size_open': 23552, 'timestamp': 1355393568}, 'repo_tags': ['test'], 'revision': '1355393568', 'updateinfo': {'checksum': '65c4f66e2808d328890505c3c2f13bb35a96f457d1c21a6346191c4dc07e6080', 'header_checksum': None, 'header_checksum_type': None, 'checksum_open': 'ded9c95e1b88197c906603b5d9693c579cb0afeade3bc7f8ec6cae06b962477d', 'checksum_open_type': 'sha256', 'checksum_type': 'sha256', 'db_version': 0, 'location_href': 'repodata/65c4f66e2808d328890505c3c2f13bb35a96f457d1c21a6346191c4dc07e6080-updateinfo.xml.gz', 'size': 55, 'size_open': 42, 'timestamp': 1354188048} } ) # Test if all mentioned files really exist self.assertTrue(os.path.isdir(yum_repo["destdir"])) for key in yum_repo: if yum_repo[key] and (key not in ("url", "destdir")): self.assertTrue(os.path.isfile(yum_repo[key])) self.assertFalse(h.mirrors) self.assertFalse(h.metalink) def test_download_repo_from_bad_url(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.BADURL) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir raised = False try: h.perform(r) except librepo.LibrepoException as err: raised = True unicode_type = unicode if sys.version_info[0] < 3 else str self.assertTrue(isinstance(err.args[1], unicode_type)) self.assertFalse(h.mirrors) self.assertFalse(h.metalink) finally: self.assertTrue(raised) def test_partial_download_repo_01(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.yumdlist = [] h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) self.assertEqual(yum_repo, {#'deltainfo': None, 'destdir': self.tmpdir, #'filelists': None, #'filelists_db': None, #'group': None, #'group_gz': None, #'origin': None, #'other': None, #'other_db': None, #'prestodelta': None, #'primary': None, #'primary_db': None, 'repomd': self.tmpdir+'/repodata/repomd.xml', #'updateinfo': None, 'url': url, 'signature': None, 'mirrorlist': None, 'metalink': None} ) # Test if all mentioned files really exist self.assertTrue(os.path.isdir(yum_repo["destdir"])) for key in yum_repo: if yum_repo[key] and (key not in ("url", "destdir")): self.assertTrue(os.path.isfile(yum_repo[key])) self.assertFalse(h.mirrors) self.assertFalse(h.metalink) def test_partial_download_repo_02(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.yumdlist = ["other", "primary"] h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) self.assertEqual(yum_repo, {#'deltainfo': None, 'destdir': self.tmpdir, #'filelists': None, #'filelists_db': None, #'group': None, #'group_gz': None, #'origin': None, 'other': self.tmpdir+'/repodata/a8977cdaa0b14321d9acfab81ce8a85e869eee32-other.xml.gz', #'other_db': None, #'prestodelta': None, 'primary': self.tmpdir+'/repodata/4543ad62e4d86337cd1949346f9aec976b847b58-primary.xml.gz', #'primary_db': None, 'repomd': self.tmpdir+'/repodata/repomd.xml', #'updateinfo': None, 'url': url, 'signature': None, 'mirrorlist': None, 'metalink': None} ) # Test if all mentioned files really exist self.assertTrue(os.path.isdir(yum_repo["destdir"])) for key in yum_repo: if yum_repo[key] and (key not in ("url", "destdir")): self.assertTrue(os.path.isfile(yum_repo[key])) self.assertFalse(h.mirrors) self.assertFalse(h.metalink) def test_partial_download_repo_03(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.yumblist = ["other", "filelists"] h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) self.assertEqual(yum_repo, { #'deltainfo': None, 'destdir': self.tmpdir, #'filelists': self.tmpdir+'/repodata/aeca08fccd3c1ab831e1df1a62711a44ba1922c9-filelists.xml.gz', 'filelists_db': self.tmpdir+'/repodata/4034dcea76c94d3f7a9616779539a4ea8cac288f-filelists.sqlite.bz2', #'group': None, #'group_gz': None, #'origin': None, #'other': self.tmpdir+'/repodata/a8977cdaa0b14321d9acfab81ce8a85e869eee32-other.xml.gz', 'other_db': self.tmpdir+'/repodata/fd96942c919628895187778633001cff61e872b8-other.sqlite.bz2', #'prestodelta': None, 'primary': self.tmpdir+'/repodata/4543ad62e4d86337cd1949346f9aec976b847b58-primary.xml.gz', 'primary_db': self.tmpdir+'/repodata/735cd6294df08bdf28e2ba113915ca05a151118e-primary.sqlite.bz2', 'repomd': self.tmpdir+'/repodata/repomd.xml', #'updateinfo': None, 'url': url, 'signature': None, 'mirrorlist': None, 'metalink': None} ) # Test if all mentioned files really exist self.assertTrue(os.path.isdir(yum_repo["destdir"])) for key in yum_repo: if yum_repo[key] and (key not in ("url", "destdir")): self.assertTrue(os.path.isfile(yum_repo[key])) self.assertFalse(h.mirrors) self.assertFalse(h.metalink) def test_partial_download_repo_with_substitution(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.yumdlist = ["foo", "primary"] h.yumslist = [("foo", "other")] h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) self.assertEqual(yum_repo, {#'deltainfo': None, 'destdir': self.tmpdir, #'filelists': None, #'filelists_db': None, #'group': None, #'group_gz': None, #'origin': None, 'other': self.tmpdir+'/repodata/a8977cdaa0b14321d9acfab81ce8a85e869eee32-other.xml.gz', #'other_db': None, #'prestodelta': None, 'primary': self.tmpdir+'/repodata/4543ad62e4d86337cd1949346f9aec976b847b58-primary.xml.gz', #'primary_db': None, 'repomd': self.tmpdir+'/repodata/repomd.xml', #'updateinfo': None, 'url': url, 'signature': None, 'mirrorlist': None, 'metalink': None} ) # Test if all mentioned files really exist self.assertTrue(os.path.isdir(yum_repo["destdir"])) for key in yum_repo: if yum_repo[key] and (key not in ("url", "destdir")): self.assertTrue(os.path.isfile(yum_repo[key])) self.assertFalse(h.mirrors) self.assertFalse(h.metalink) def test_download_repo_01_without_result_object(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True r = h.perform() yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) def test_download_repo_01_with_checksum_check(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) def test_download_corrupted_repo_01_with_checksum_check(self): h = librepo.Handle() r = librepo.Result() url = "%s%s%s" % (self.MOCKURL, config.HARMCHECKSUM % "primary.xml", config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True self.assertRaises(librepo.LibrepoException, h.perform, (r)) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) def test_download_repo_with_gpg_check(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.gpgcheck = True h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) self.assertTrue("signature" in yum_repo and yum_repo["signature"]) self.assertTrue(self.tmpdir+'/repodata/repomd.xml.asc' == yum_repo["signature"] ) self.assertTrue(os.path.isfile(yum_repo["signature"])) def test_download_repo_with_gpg_check_bad_signature(self): h = librepo.Handle() r = librepo.Result() url = "%s%s%s" % (self.MOCKURL, config.BADGPG, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.gpgcheck = True self.assertRaises(librepo.LibrepoException, h.perform, (r)) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) self.assertTrue("signature" not in yum_repo or yum_repo["signature"]) def test_download_repo_01_with_missing_file(self): h = librepo.Handle() r = librepo.Result() url = "%s%s%s" % (self.MOCKURL, config.MISSINGFILE % "primary.xml", config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir self.assertRaises(librepo.LibrepoException, h.perform, (r)) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) self.assertTrue(os.path.getsize(yum_repo["primary"]) == 0) def test_download_repo_01_with_missing_unwanted_file(self): h = librepo.Handle() r = librepo.Result() url = "%s%s%s" % (self.MOCKURL, config.MISSINGFILE % "primary.xml", config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.yumdlist = ["other"] h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) def test_bad_mirrorlist_url(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.BADURL) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir self.assertRaises(librepo.LibrepoException, h.perform, (r)) # Metalink tests def test_download_only_metalink(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.METALINK_GOOD_01) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.fetchmirrors = True h.perform(r) self.assertEqual(h.mirrors, ['http://127.0.0.1:%d/yum/static/01/' % self.PORT]) self.assertEqual(h.metalink, {'timestamp': 1347459931, 'hashes': [ ('md5', 'f76409f67a84bcd516131d5cc98e57e1'), ('sha1', '75125e73304c21945257d9041a908d0d01d2ca16'), ('sha256', 'bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a'), ('sha512', 'e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0')], 'size': 2621, 'urls': [{ 'url': 'http://127.0.0.1:%d/yum/static/01/repodata/repomd.xml' % self.PORT, 'type': 'http', 'protocol': 'http', 'location': 'CZ', 'preference': 100}], 'filename': 'repomd.xml'} ) def test_download_repo_01_via_metalink_with_alternates(self): h = librepo.Handle() h.metalinkurl = "%s%s" % (self.MOCKURL, config.METALINK_WITH_ALTERNATES) h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True r = h.perform() yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertEqual(yum_repo["url"], "http://127.0.0.1:%d/yum/static/01/" % self.PORT) self.assertTrue(yum_repomd) self.assertEqual(h.mirrors, ['http://127.0.0.1:%d/yum/static/01/' % self.PORT]) self.assertEqual(h.metalink, {'timestamp': 1381706941, 'hashes': [ ('md5', 'bad'), ('sha1', 'bad'), ('sha256', 'bad'), ('sha512', 'bad')], 'size': 4761, 'urls': [{ 'url': 'http://127.0.0.1:%d/yum/static/01/repodata/repomd.xml' % self.PORT, 'type': 'http', 'protocol': 'http', 'location': 'CZ', 'preference': 100}], 'filename': 'repomd.xml', 'alternates': [{ 'timestamp': 1347459931, 'hashes': [ ('md5', 'f76409f67a84bcd516131d5cc98e57e1'), ('sha1', '75125e73304c21945257d9041a908d0d01d2ca16'), ('sha256', 'bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a'), ('sha512', 'e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0') ], 'size': 2621}, { 'timestamp': 123, 'hashes': [ ('sha1', 'foobar'), ], 'size': 456}] } ) def test_download_repo_01_via_metalink_01(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.METALINK_GOOD_01) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertEqual(yum_repo["url"], "http://127.0.0.1:%d/yum/static/01/" % self.PORT) self.assertTrue(yum_repomd) self.assertEqual(h.mirrors, ['http://127.0.0.1:%d/yum/static/01/' % self.PORT]) self.assertEqual(h.metalink, {'timestamp': 1347459931, 'hashes': [ ('md5', 'f76409f67a84bcd516131d5cc98e57e1'), ('sha1', '75125e73304c21945257d9041a908d0d01d2ca16'), ('sha256', 'bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a'), ('sha512', 'e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0')], 'size': 2621, 'urls': [{ 'url': 'http://127.0.0.1:%d/yum/static/01/repodata/repomd.xml' % self.PORT, 'type': 'http', 'protocol': 'http', 'location': 'CZ', 'preference': 100}], 'filename': 'repomd.xml'} ) def test_download_repo_01_via_metalink_02(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.METALINK_GOOD_02) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) def test_download_repo_01_via_metalink_badfilename(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.METALINK_BADFILENAME) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir self.assertRaises(librepo.LibrepoException, h.perform, (r)) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertEqual(yum_repo, None) self.assertEqual(yum_repomd, None) def test_download_repo_01_via_metalink_badchecksum(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.METALINK_BADCHECKSUM) h.metalinkurl = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True self.assertRaises(librepo.LibrepoException, h.perform, (r)) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo["metalink"]) # All other values shoud be None, [], {} or equivalent for key in yum_repo: if key == "metalink": continue self.assertFalse(yum_repo[key]) for key in yum_repomd: self.assertFalse(yum_repomd[key]) def test_download_repo_01_via_metalink_nourls(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.METALINK_NOURLS) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir self.assertRaises(librepo.LibrepoException, h.perform, (r)) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertEqual(yum_repo, None) self.assertEqual(yum_repomd, None) def test_download_repo_01_via_metalink_badfirsturl(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.METALINK_BADFIRSTURL) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) self.assertEqual(yum_repo["url"], "http://127.0.0.1:%d/yum/static/01/" % self.PORT) # Test if all mentioned files really exist self.assertTrue(os.path.isdir(yum_repo["destdir"])) for key in yum_repo: if yum_repo[key] and (key not in ("url", "destdir")): self.assertTrue(os.path.isfile(yum_repo[key])) def test_download_repo_01_via_metalink_badfirsturl_maxmirrortries(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.METALINK_BADFIRSTHOST) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.maxmirrortries = 1 # Because first host is bad and maxmirrortries == 1 # Download should fail self.assertRaises(librepo.LibrepoException, h.perform, (r)) def test_download_repo_01_via_metalink_badfirsthost_fastestmirror(self): time.sleep(0.5) h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.METALINK_BADFIRSTHOST) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.fastestmirror = True h.fastestmirrortimeout = 5.0 h.maxmirrortries = 1 # First host is bad, but fastestmirror is used and thus # working mirror should be added to the first position # and download should be successful even if maxmirrortries # is equal to 1. h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) self.assertEqual(yum_repo["url"], "http://127.0.0.1:%d/yum/static/01/" % self.PORT) # Test if all mentioned files really exist self.assertTrue(os.path.isdir(yum_repo["destdir"])) for key in yum_repo: if yum_repo[key] and (key not in ("url", "destdir")): self.assertTrue(os.path.isfile(yum_repo[key])) def test_download_repo_01_via_metalink_badfirsthost_fastestmirror_with_cache(self): time.sleep(0.5) h = librepo.Handle() r = librepo.Result() cache = os.path.join(self.tmpdir, "fastestmirror.cache") self.assertFalse(os.path.exists(cache)) url = "%s%s" % (self.MOCKURL, config.METALINK_BADFIRSTHOST) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.fastestmirror = True h.fastestmirrortimeout = 5.0 h.fastestmirrorcache = cache h.maxmirrortries = 1 # First host is bad, but fastestmirror is used and thus # working mirror should be added to the first position # and download should be successful even if maxmirrortries # is equal to 1. h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) self.assertEqual(yum_repo["url"], "http://127.0.0.1:%d/yum/static/01/" % self.PORT) self.assertTrue(os.path.exists(cache)) shutil.rmtree(os.path.join(self.tmpdir, "repodata")) # Try again, this time, fastestmirror cache should be used h.perform() yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) self.assertEqual(yum_repo["url"], "http://127.0.0.1:%d/yum/static/01/" % self.PORT) self.assertTrue(os.path.exists(cache)) def test_download_repo_01_via_metalink_firsturlhascorruptedfiles(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.METALINK_FIRSTURLHASCORRUPTEDFILES) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) self.assertEqual(yum_repo["url"], "http://127.0.0.1:%d/yum/harm_checksum/primary.xml/static/01/" % self.PORT) # Test if all mentioned files really exist self.assertTrue(os.path.isdir(yum_repo["destdir"])) for key in yum_repo: if yum_repo[key] and (key not in ("url", "destdir")): self.assertTrue(os.path.isfile(yum_repo[key])) def test_download_repo_01_with_baseurl_and_metalink_specified_only_fetchmirrors(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] url = "%s%s" % (self.MOCKURL, config.METALINK_GOOD_01) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.fetchmirrors = True h.perform(r) self.assertEqual(h.mirrors, ['http://127.0.0.1:%d/yum/static/01/' % self.PORT]) self.assertEqual(h.metalink, {'timestamp': 1347459931, 'hashes': [ ('md5', 'f76409f67a84bcd516131d5cc98e57e1'), ('sha1', '75125e73304c21945257d9041a908d0d01d2ca16'), ('sha256', 'bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a'), ('sha512', 'e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0')], 'size': 2621, 'urls': [{ 'url': 'http://127.0.0.1:%d/yum/static/01/repodata/repomd.xml' % self.PORT, 'type': 'http', 'protocol': 'http', 'location': 'CZ', 'preference': 100}], 'filename': 'repomd.xml'} ) def test_download_repo_01_with_baseurl_and_metalink_specified(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] url = "%s%s" % (self.MOCKURL, config.METALINK_GOOD_01) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.fastestmirror = True # XXX h.perform(r) self.assertEqual(h.mirrors, ['http://127.0.0.1:%d/yum/static/01/' % self.PORT]) self.assertEqual(h.metalink, {'timestamp': 1347459931, 'hashes': [ ('md5', 'f76409f67a84bcd516131d5cc98e57e1'), ('sha1', '75125e73304c21945257d9041a908d0d01d2ca16'), ('sha256', 'bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a'), ('sha512', 'e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0')], 'size': 2621, 'urls': [{ 'url': 'http://127.0.0.1:%d/yum/static/01/repodata/repomd.xml' % self.PORT, 'type': 'http', 'protocol': 'http', 'location': 'CZ', 'preference': 100}], 'filename': 'repomd.xml'} ) # Mirrorlist tests def test_download_only_mirrorlist(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.MIRRORLIST_GOOD_01) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.fetchmirrors = True h.perform(r) self.assertEqual(h.mirrors, ['http://127.0.0.1:%d/yum/static/01/' % self.PORT]) self.assertEqual(h.metalink, None) def test_download_repo_01_via_mirrorlist_01(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.MIRRORLIST_GOOD_01) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertEqual(yum_repo["url"], "http://127.0.0.1:%d/yum/static/01/" % self.PORT) self.assertTrue(yum_repomd) def test_download_repo_01_via_mirrorlist_02(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.MIRRORLIST_GOOD_02) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertEqual(yum_repo["url"], "http://127.0.0.1:%d/yum/static/01/" % self.PORT) self.assertTrue(yum_repomd) def test_download_repo_01_via_mirrorlist_nourls(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.MIRRORLIST_NOURLS) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir self.assertRaises(librepo.LibrepoException, h.perform, (r)) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertEqual(yum_repo, None) self.assertEqual(yum_repomd, None) def test_download_repo_01_via_mirrorlist_badfirsturl(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.MIRRORLIST_BADFIRSTURL) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) self.assertEqual(yum_repo["url"], "http://127.0.0.1:%d/yum/static/01/" % self.PORT) # Test if all mentioned files really exist self.assertTrue(os.path.isdir(yum_repo["destdir"])) for key in yum_repo: if yum_repo[key] and (key not in ("url", "destdir")): self.assertTrue(os.path.isfile(yum_repo[key])) def test_download_repo_01_via_mirrorlist_firsturlhascorruptedfiles(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.MIRRORLIST_FIRSTURLHASCORRUPTEDFILES) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) self.assertEqual(yum_repo["url"], "http://127.0.0.1:%d/yum/harm_checksum/primary.xml/static/01/" % self.PORT) # Test if all mentioned files really exist self.assertTrue(os.path.isdir(yum_repo["destdir"])) for key in yum_repo: if yum_repo[key] and (key not in ("url", "destdir")): self.assertTrue(os.path.isfile(yum_repo[key])) def test_download_repo_01_via_mirrorlist_firsturlhascorruptedfiles_maxmirrortries_enabled(self): """Download should fails on the first mirror (one file has a bad checksum). Other mirrors have the file with a good checksum, but option LRO_MAXMIRRORTRIES should prevent trying of other mirrors.""" h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.MIRRORLIST_FIRSTURLHASCORRUPTEDFILES) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.maxmirrortries = 1 self.assertRaises(librepo.LibrepoException, h.perform, (r)) def test_download_repo_01_via_mirrorlist_firsturlhascorruptedfiles_maxmirrortries_enabled_2(self): """Download should fails on the first mirror (one file has a bad checksum). Other mirrors have the file with a good checksum. LRO_MAXMIRRORTRIES should allow try one next mirror. Thus repo should be downloaded without error.""" h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.MIRRORLIST_FIRSTURLHASCORRUPTEDFILES) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.maxmirrortries = 2 h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) self.assertEqual(yum_repo["url"], "http://127.0.0.1:%d/yum/harm_checksum/primary.xml/static/01/" % self.PORT) # Test if all mentioned files really exist self.assertTrue(os.path.isdir(yum_repo["destdir"])) for key in yum_repo: if yum_repo[key] and (key not in ("url", "destdir")): self.assertTrue(os.path.isfile(yum_repo[key])) def test_download_repo_01_with_baseurl_and_mirrorlist_specified_only_fetchmirrors(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] url = "%s%s" % (self.MOCKURL, config.MIRRORLIST_GOOD_01) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.fetchmirrors = True h.perform(r) self.assertEqual(h.mirrors, ['http://127.0.0.1:%d/yum/static/01/' % self.PORT]) self.assertEqual(h.metalink, None) def test_download_repo_01_with_baseurl_and_mirrorlist_specified(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] url = "%s%s" % (self.MOCKURL, config.MIRRORLIST_GOOD_01) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.perform(r) self.assertEqual(h.mirrors, ['http://127.0.0.1:%d/yum/static/01/' % self.PORT]) self.assertEqual(h.metalink, None) # Update test def test_download_and_update_repo_01(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.MIRRORLIST_FIRSTURLHASCORRUPTEDFILES) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.yumdlist = [None] h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) self.assertEqual(yum_repo["url"], "http://127.0.0.1:%d/yum/harm_checksum/primary.xml/static/01/" % self.PORT) # Test that only repomd.xml has a path self.assertTrue(os.path.isdir(yum_repo["destdir"])) self.assertTrue(os.path.exists(yum_repo["repomd"])) for key in yum_repo: if yum_repo[key] and (key not in ("repomd", "url", "destdir", "mirrorlist")): self.assertTrue(yum_repo[key] == None) # Update repo h.update = True h.yumdlist = ["primary"] h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) # Test that only repomd.xml and primary has a path in yum_repo self.assertTrue(os.path.isdir(yum_repo["destdir"])) self.assertTrue(os.path.exists(yum_repo["repomd"])) self.assertTrue(os.path.exists(yum_repo["primary"])) for key in yum_repo: if yum_repo[key] and (key not in ("repomd", "primary", "url", "destdir", "mirrorlist")): self.assertTrue(yum_repo[key] == None) # Base Auth test def test_download_repo_01_from_base_auth_secured_web_01(self): h = librepo.Handle() r = librepo.Result() url = "%s%s%s" % (self.MOCKURL, config.AUTHBASIC, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True self.assertRaises(librepo.LibrepoException, h.perform, (r)) def test_download_repo_01_from_base_auth_secured_web_02(self): h = librepo.Handle() r = librepo.Result() url = "%s%s%s" % (self.MOCKURL, config.AUTHBASIC, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.httpauth = True h.userpwd = "%s:%s" % (config.AUTH_USER, config.AUTH_PASS) h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) # Progressbar test def test_download_repo_01_with_progressbar(self): data = {"ttd": 0, "d": 0, "calls": 0} def cb(data, total_to_download, downloaded): data["ttd"] = total_to_download data["d"] = downloaded data["calls"] = data["calls"] + 1 h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.progressdata = data h.progresscb = cb h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) self.assertTrue(data["calls"] > 0) self.assertTrue(data["ttd"] == data["d"] or data["ttd"] == 0) # Var substitution test def test_download_repo_01_with_url_substitution(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH_VAR) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.checksum = True h.varsub = config.REPO_YUM_01_VARSUB_LIST h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertTrue(yum_repo) self.assertTrue(yum_repomd) self.assertTrue(yum_repo["url"].endswith(config.REPO_YUM_01_PATH)) def test_download_repo_01_mirrorlist_substitution(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.MIRRORLIST_VARED) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.fetchmirrors = True h.varsub = config.MIRRORLIST_VARED_LIST h.perform(r) self.assertEqual(h.mirrors, ['http://127.0.0.1:%d/yum/static/01/' % self.PORT]) self.assertEqual(h.metalink, None) def test_download_repo_01_mirrorlist_with_url_substitution(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.MIRRORLIST_VARSUB) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.fetchmirrors = True h.varsub = config.MIRRORLIST_VARSUB_LIST h.perform(r) self.assertEqual(h.mirrors, ['http://127.0.0.1:%d/yum/static/01/' % self.PORT]) self.assertEqual(h.metalink, None) def test_download_repo_01_metalink_with_url_substitution(self): h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.METALINK_VARSUB) h.mirrorlist = url h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.fetchmirrors = True h.varsub = config.METALINK_VARSUB_LIST h.perform(r) self.assertEqual(h.mirrors, ['http://127.0.0.1:%d/yum/static/01/' % self.PORT]) self.assertEqual(h.metalink["urls"], [{ 'url': 'http://127.0.0.1:%d/yum/static/$version/repodata/repomd.xml' % self.PORT, 'type': 'http', 'protocol': 'http', 'location': 'CZ', 'preference': 100} ]) def test_download_with_gpgcheck_enabled_but_without_signature(self): # At first, download whole repository h = librepo.Handle() r = librepo.Result() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_02_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.gpgcheck = True self.assertRaises(librepo.LibrepoException, h.perform, (r)) def test_download_with_custom_http_headers(self): h = librepo.Handle() url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h.urls = [url] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir headers = ["Accept: audio/mpeg"] h.httpheader = headers del headers h.perform() def test_download_with_local_enabled(self): url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) # Make local cache h_l = librepo.Handle() h_l.repotype = librepo.YUMREPO h_l.urls = [url] h_l.destdir = self.tmpdir h_l.perform() self.assertFalse(h_l.mirrors) # No mirrors should be listed # Create a handle for the local cache h = librepo.Handle() h.repotype = librepo.YUMREPO h.urls = [self.tmpdir] h.metalinkurl = "%s%s" % (self.MOCKURL, config.METALINK_GOOD_01) h.local = True h.perform() self.assertFalse(h.mirrors) # Remote metalik is specified # No mirrors should be listed # Disable local h.local = False # Try to download something (don't care that it doesn't exist) librepo.download_packages([librepo.PackageTarget("Foo", handle=h)]) self.assertTrue(h.mirrors) # List of mirrors should be re-initialized # and should contain mirrors from the metalink def test_download_with_offline_enabled_01(self): url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) h = librepo.Handle() h.repotype = librepo.YUMREPO h.urls = [url] h.destdir = self.tmpdir h.offline = True # Only one remote URL is specified - this should fail self.assertRaises(librepo.LibrepoException, h.perform) shutil.rmtree(os.path.join(self.tmpdir, "repodata")) # But everything should be ok when we disable the option h.offline = False h.perform() def test_download_with_offline_enabled_02(self): url = "%s%s" % (self.MOCKURL, config.REPO_YUM_01_PATH) dir_01 = os.path.join(self.tmpdir, "01") dir_02 = os.path.join(self.tmpdir, "02") os.makedirs(dir_01) os.makedirs(dir_02) # Prepare local cache h_l = librepo.Handle() h_l.repotype = librepo.YUMREPO h_l.urls = [url] h_l.destdir = dir_01 h_l.perform() # Let's work offline h = librepo.Handle() h.repotype = librepo.YUMREPO h.urls = ["http://foo.bar/xyz", dir_01] h.metalinkurl = "http://foo.bar/mtl" h.mirrorlisturl = "http://foo.bar/mrl" h.fastestmirror = True h.destdir = dir_02 h.offline = True # Bad URLs for metalink and mirrorlist should be ok, they # should be ignored - we should work offline # First remote URL set in urls option should be ignored # Fastestmirror should be skipped h.perform() def test_download_with_offline_enabled_03(self): url_mtl = "%s%s" % (self.MOCKURL, config.METALINK_GOOD_01) url_mrl = "%s%s" % (self.MOCKURL, config.MIRRORLIST_GOOD_01) h = librepo.Handle() h.repotype = librepo.YUMREPO h.metalinkurl = url_mtl h.mirrorlisturl = url_mrl h.fastestmirror = True h.offline = True h.fetchmirrors = True h.destdir = self.tmpdir # We only want to fetch mirrors # But both metalink and mirrorlist is specified by remote URL # Output mirrorlist should be empty h.perform() self.assertFalse(h.mirrors) # But when we disable offline, it should work h.offline = False h.perform() self.assertTrue(h.mirrors) def test_download_with_offline_enabled_04(self): url_mtl = "%s%s" % (self.MOCKURL, config.METALINK_GOOD_01) url_mrl = "%s%s" % (self.MOCKURL, config.MIRRORLIST_GOOD_01) dir_01 = os.path.join(self.tmpdir, "01") dir_02 = os.path.join(self.tmpdir, "02") os.makedirs(dir_01) os.makedirs(dir_02) EXP_MRS = [u'http://127.0.0.1:%s/yum/static/01/' % self.PORT, u'http://127.0.0.1:%s/yum/static/01/' % self.PORT] # 1) At first, work online h = librepo.Handle() h.repotype = librepo.YUMREPO h.metalinkurl = url_mtl h.mirrorlisturl = url_mrl h.fastestmirror = True h.offline = False h.destdir = dir_01 h.perform() self.assertTrue(h.mirrors) # 2) Then switch to offline mode h.offline = True h.destdir = dir_02 # We should not be able to download the repodata self.assertRaises(librepo.LibrepoException, h.perform) # Mirrors should still be there from the first try self.assertTrue(h.mirrors) self.assertEqual(h.mirrors, EXP_MRS) # 3) Let's stay offline but use repodata from first try shutil.rmtree(os.path.join(dir_02, "repodata")) h.urls = [dir_01] h.perform() # Mirrors should still be there from the first try self.assertTrue(h.mirrors) self.assertEqual(h.mirrors, EXP_MRS) # 4) Still offline, but mirrors get reset shutil.rmtree(os.path.join(dir_02, "repodata")) h.metalinkurl = url_mtl h.mirrorlisturl = url_mrl h.perform() # Mirrors should be reset and thus empty self.assertFalse(h.mirrors) # 5) Create a brand new handle and load a local mirrorlists shutil.rmtree(os.path.join(dir_02, "repodata")) h = librepo.Handle() h.repotype = librepo.YUMREPO h.metalinkurl = os.path.join(dir_01, "metalink.xml") h.mirrorlisturl = os.path.join(dir_01, "mirrorlist") h.fastestmirror = True h.offline = True h.destdir = dir_02 # Perform should fail, because we are working offline self.assertRaises(librepo.LibrepoException, h.perform) # The local mirrorlists should be loaded self.assertTrue(h.mirrors) self.assertEqual(h.mirrors, EXP_MRS) librepo-1.12.1/tests/python/tests/test_yum_repo_locating.py000066400000000000000000000223401372027637700242240ustar00rootroot00000000000000import os.path import tempfile import shutil import unittest import librepo from tests.base import Context, TestCase, TEST_DATA # Repositories used in download tests #REPOS_YUM = "tests/python/tests/servermock/yum_mock/static/" REPO_YUM_01_PATH = TEST_DATA+"/repo_yum_01/" REPO_YUM_02_PATH = TEST_DATA+"/repo_yum_02/" PUB_KEY = TEST_DATA+"/key.pub" MIRRORLIST = TEST_DATA+"/mirrorlist" METALINK = TEST_DATA+"/metalink.xml" class TestCaseYumRepoLocating(TestCase): def setUp(self): self.tmpdir = tempfile.mkdtemp(prefix="librepotest-") # Import public key into the temporary gpg keyring self._gnupghome = os.environ.get('GNUPGHOME') gpghome = os.path.join(self.tmpdir, "keyring") os.mkdir(gpghome, 0o700) os.environ['GNUPGHOME'] = gpghome self.ctx = Context() self.ctx.op_import(open(PUB_KEY, 'rb')) def tearDown(self): if self._gnupghome is None: os.environ.pop('GNUPGHOME') else: os.environ['GNUPGHOME'] = self._gnupghome shutil.rmtree(self.tmpdir, True) def test_read_mirrorlist(self): h = librepo.Handle() r = librepo.Result() h.mirrorlist = MIRRORLIST h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.fetchmirrors = True h.perform(r) self.assertEqual(h.mirrors, ['http://127.0.0.1:5000/yum/static/01/']) self.assertEqual(h.metalink, None) def test_read_metalink(self): h = librepo.Handle() r = librepo.Result() h.mirrorlist = METALINK h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.fetchmirrors = True h.perform(r) self.assertEqual(h.mirrors, ['http://127.0.0.1:5000/yum/static/01/']) self.assertEqual(h.metalink, {'timestamp': 1347459931, 'hashes': [ ('md5', 'f76409f67a84bcd516131d5cc98e57e1'), ('sha1', '75125e73304c21945257d9041a908d0d01d2ca16'), ('sha256', 'bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a'), ('sha512', 'e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0')], 'size': 2621, 'urls': [{ 'url': 'http://127.0.0.1:5000/yum/static/01/repodata/repomd.xml', 'type': 'http', 'protocol': 'http', 'location': 'CZ', 'preference': 100}], 'filename': 'repomd.xml'}) def test_read_metalink_of_local_repo(self): # At first, download whole repository # Check if local metalink.xml will be copied as well h = librepo.Handle() r = librepo.Result() h.urls = [REPO_YUM_01_PATH] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.gpgcheck = True h.perform(r) yum_repo_downloaded = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd_downloaded = r.getinfo(librepo.LRR_YUM_REPOMD) self.assertFalse(yum_repo_downloaded["mirrorlist"]) self.assertTrue(yum_repo_downloaded["metalink"]) self.assertTrue(yum_repo_downloaded["metalink"].endswith("metalink.xml")) # Now try to read metalink of the repository h = librepo.Handle() r = librepo.Result() h.urls = [self.tmpdir] h.repotype = librepo.LR_YUMREPO h.local = True h.fetchmirrors = True h.perform(r) self.assertEqual(h.mirrors, ['http://127.0.0.1:5000/yum/static/01/']) self.assertEqual(h.metalink, {'timestamp': 1347459931, 'hashes': [ ('md5', 'f76409f67a84bcd516131d5cc98e57e1'), ('sha1', '75125e73304c21945257d9041a908d0d01d2ca16'), ('sha256', 'bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a'), ('sha512', 'e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0')], 'size': 2621, 'urls': [{ 'url': 'http://127.0.0.1:5000/yum/static/01/repodata/repomd.xml', 'type': 'http', 'protocol': 'http', 'location': 'CZ', 'preference': 100}], 'filename': 'repomd.xml'} ) def test_locate_repo_01(self): # At first, download whole repository h = librepo.Handle() r = librepo.Result() h.urls = [REPO_YUM_01_PATH] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.gpgcheck = True h.perform(r) yum_repo_downloaded = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd_downloaded = r.getinfo(librepo.LRR_YUM_REPOMD) # Now try to localize the existing repository and all its files h = librepo.Handle() r = librepo.Result() h.urls = [self.tmpdir] h.repotype = librepo.LR_YUMREPO h.local = True h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) # Compare results yum_repo_downloaded["url"] = None self.assertEqual(yum_repo, yum_repo_downloaded) self.assertEqual(yum_repomd, yum_repomd_downloaded) def test_locate_repo_02(self): # At first, download whole repository h = librepo.Handle() r = librepo.Result() h.urls = [REPO_YUM_02_PATH] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.gpgcheck = False h.perform(r) yum_repo_downloaded = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd_downloaded = r.getinfo(librepo.LRR_YUM_REPOMD) # Now try to localize the existing repository and all its files h = librepo.Handle() r = librepo.Result() h.urls = [self.tmpdir] h.repotype = librepo.LR_YUMREPO h.local = True h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) # Compare results yum_repo_downloaded["url"] = None self.assertEqual(yum_repo, yum_repo_downloaded) self.assertEqual(yum_repomd, yum_repomd_downloaded) def test_locate_incomplete_repo_01(self): # At first, download only some files from the repository h = librepo.Handle() r = librepo.Result() h.urls = [REPO_YUM_01_PATH] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.gpgcheck = True h.yumdlist = ["primary"] h.perform(r) yum_repo_downloaded = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd_downloaded = r.getinfo(librepo.LRR_YUM_REPOMD) # Now try to localize the existing repository h = librepo.Handle() r = librepo.Result() h.urls = [self.tmpdir] h.repotype = librepo.LR_YUMREPO h.local = True self.assertRaises(librepo.LibrepoException, h.perform, (r)) def test_locate_incomplete_repo_01_2(self): # At first, download whole repository h = librepo.Handle() r = librepo.Result() h.urls = [REPO_YUM_01_PATH] h.repotype = librepo.LR_YUMREPO h.destdir = self.tmpdir h.gpgcheck = True h.yumdlist = ["primary"] h.perform(r) yum_repo_downloaded = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd_downloaded = r.getinfo(librepo.LRR_YUM_REPOMD) # Now try to localize the existing repository and all its files h = librepo.Handle() r = librepo.Result() h.urls = [self.tmpdir] h.repotype = librepo.LR_YUMREPO h.local = True h.ignoremissing = True h.perform(r) yum_repo = r.getinfo(librepo.LRR_YUM_REPO) yum_repomd = r.getinfo(librepo.LRR_YUM_REPOMD) # Compare results yum_repo_downloaded["url"] = None self.assertEqual(yum_repo, yum_repo_downloaded) self.assertEqual(yum_repomd, yum_repomd_downloaded) def test_locate_with_gpgcheck_enabled_but_without_signature(self): # At first, download whole repository h = librepo.Handle() h.urls = [REPO_YUM_02_PATH] h.repotype = librepo.LR_YUMREPO h.gpgcheck = True h.local = True self.assertRaises(librepo.LibrepoException, h.perform) def test_locate_with_metalink_and_mirrorlist_urls_set(self): # See: https://github.com/Tojaj/librepo/issues/41 h = librepo.Handle() h.urls = [REPO_YUM_02_PATH] # These bad URLs should not raise any error because # librepo shoudn't try to download them h.metalinkurl = "xyz://really-bad-url-sf987243kj.com" h.mirrorlisturl = "xyz://really-bad-url-mi-qcwmi.com" h.repotype = librepo.LR_YUMREPO h.local = True h.perform() def test_locate_with_offline(self): # Offline and Local options shouldn't collide in any way h = librepo.Handle() h.urls = [REPO_YUM_02_PATH] h.metalinkurl = "xyz://really-bad-url-sf987243kj.com" h.mirrorlisturl = "xyz://really-bad-url-mi-qcwmi.com" h.repotype = librepo.LR_YUMREPO h.local = True h.offline = True h.perform() librepo-1.12.1/tests/run_tests.sh.in000077500000000000000000000004621372027637700174040ustar00rootroot00000000000000#!/bin/bash # Next line is a hack # Builds for epel6 doesn't have rpath setted and # tests fails with a "librepo_c.so.0: cannot open shared # object file: No such file or directory" error message. export "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/librepo/:" ./test_main "${CMAKE_CURRENT_SOURCE_DIR}/test_data/" librepo-1.12.1/tests/test_checksum.c000066400000000000000000000142531372027637700174220ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "librepo/util.h" #include "librepo/checksum.h" #include "fixtures.h" #include "testsys.h" #include "test_checksum.h" #define CHKS_CONTENT_00 "" #define CHKS_VAL_00_MD5 "d41d8cd98f00b204e9800998ecf8427e" #define CHKS_VAL_00_SHA1 "da39a3ee5e6b4b0d3255bfef95601890afd80709" #define CHKS_VAL_00_SHA224 "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" #define CHKS_VAL_00_SHA256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" #define CHKS_VAL_00_SHA384 "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b" #define CHKS_VAL_00_SHA512 "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" #define CHKS_CONTENT_01 "foo\nbar\n\n" #define CHKS_VAL_01_MD5 "8b03476e0c92a803ed47e0817b2717ed" #define CHKS_VAL_01_SHA1 "db957786773c3b2ae5fc2d2c839bab1bbe5dc09e" #define CHKS_VAL_01_SHA224 "00f288c08f7f745d59252f875007b6734bb2f0aac295463cf242aac0" #define CHKS_VAL_01_SHA256 "242218bd1466c1ab22e501d1e86bc906bacfd6943ded790ea8b2b040e618d81b" #define CHKS_VAL_01_SHA384 "1f9d03f7a9fc1c22bfe114e2b58c334fcf58a07df78b4b0e942a28582ebf6489823e242492b2e0e5df1ce995e918c80d" #define CHKS_VAL_01_SHA512 "704861d613afe433160d9b5aa6870e0dd96f5e56c4976f1fe39f4f648e37517ad7374209034290284949b4218ab0c8d8860f941c884cad47a61f208803128049" static void build_test_file(const char *filename, const char *content) { FILE *fp = fopen(filename, "w"); size_t len = strlen(content); fail_if(fp == NULL); fail_unless(fwrite(content, 1, len, fp) == len); fclose(fp); } static void test_checksum(const char *filename, LrChecksumType ch_type, char *expected) { int fd; char *checksum; GError *tmp_err = NULL; fd = open(filename, O_RDONLY); fail_if(fd < 0); checksum = lr_checksum_fd(ch_type, fd, &tmp_err); fail_if(checksum == NULL); fail_if(tmp_err); fail_if(strcmp(checksum, expected), "Checksum is %s instead of %s", checksum, expected); lr_free(checksum); close(fd); } START_TEST(test_checksum_fd) { char *file; file = lr_pathconcat(test_globals.tmpdir, "/test_checksum", NULL); /* Empty file */ build_test_file(file, CHKS_CONTENT_00); test_checksum(file, LR_CHECKSUM_MD5, CHKS_VAL_00_MD5); test_checksum(file, LR_CHECKSUM_SHA1, CHKS_VAL_00_SHA1); test_checksum(file, LR_CHECKSUM_SHA224, CHKS_VAL_00_SHA224); test_checksum(file, LR_CHECKSUM_SHA256, CHKS_VAL_00_SHA256); test_checksum(file, LR_CHECKSUM_SHA384, CHKS_VAL_00_SHA384); test_checksum(file, LR_CHECKSUM_SHA512, CHKS_VAL_00_SHA512); /* File with some content */ build_test_file(file, CHKS_CONTENT_01); test_checksum(file, LR_CHECKSUM_MD5, CHKS_VAL_01_MD5); test_checksum(file, LR_CHECKSUM_SHA1, CHKS_VAL_01_SHA1); test_checksum(file, LR_CHECKSUM_SHA224, CHKS_VAL_01_SHA224); test_checksum(file, LR_CHECKSUM_SHA256, CHKS_VAL_01_SHA256); test_checksum(file, LR_CHECKSUM_SHA384, CHKS_VAL_01_SHA384); test_checksum(file, LR_CHECKSUM_SHA512, CHKS_VAL_01_SHA512); fail_if(remove(file) != 0, "Cannot delete temporary test file"); lr_free(file); } END_TEST START_TEST(test_cached_checksum) { FILE *f; int fd, ret; gboolean checksum_ret, matches; ssize_t attr_ret; char *filename; static char *expected = "d78931fcf2660108eec0d6674ecb4e02401b5256a6b5ee82527766ef6d198c67"; struct stat st; char *key; char buf[256]; GError *tmp_err = NULL; filename = lr_pathconcat(test_globals.tmpdir, "/test_checksum", NULL); f = fopen(filename, "w"); fail_if(f == NULL); fwrite("foo\nbar\n", 1, 8, f); fclose(f); // Assert no cached checksum exists ret = stat(filename, &st); fail_if(ret != 0); key = g_strdup_printf("user.Zif.MdChecksum[%llu]", (unsigned long long) st.st_mtime); #if __APPLE__ attr_ret = getxattr(filename, key, &buf, sizeof(buf), 0, 0); #else attr_ret = getxattr(filename, key, &buf, sizeof(buf)); #endif lr_free(key); fail_if(attr_ret != -1); // Cached checksum should not exists // Calculate checksum fd = open(filename, O_RDONLY); fail_if(fd < 0); checksum_ret = lr_checksum_fd_cmp(LR_CHECKSUM_SHA256, fd, expected, 1, &matches, &tmp_err); fail_if(tmp_err); fail_if(!checksum_ret); fail_if(!matches); close(fd); // Assert cached checksum exists ret = stat(filename, &st); fail_if(ret != 0); key = g_strdup_printf("user.Zif.MdChecksum[%llu]", (unsigned long long) st.st_mtime); #if __APPLE__ attr_ret = getxattr(filename, key, &buf, sizeof(buf), 0, 0); #else attr_ret = getxattr(filename, key, &buf, sizeof(buf)); #endif lr_free(key); if (attr_ret == -1) { // Error encountered if (errno == ENOTSUP) { // Extended attributes are not supported goto exit_label; } // Any other errno means fail fail_if(attr_ret == -1); } else { fail_if(strcmp(buf, expected)); } // Calculate checksum again (cached shoud be used this time) fd = open(filename, O_RDONLY); fail_if(fd < 0); checksum_ret = lr_checksum_fd_cmp(LR_CHECKSUM_SHA256, fd, expected, 1, &matches, &tmp_err); fail_if(tmp_err); fail_if(!checksum_ret); fail_if(!matches); close(fd); exit_label: lr_free(filename); } END_TEST Suite * checksum_suite(void) { Suite *s = suite_create("cheksum"); TCase *tc = tcase_create("Main"); tcase_add_test(tc, test_checksum_fd); tcase_add_test(tc, test_cached_checksum); suite_add_tcase(s, tc); return s; } librepo-1.12.1/tests/test_checksum.h000066400000000000000000000001601372027637700174170ustar00rootroot00000000000000#ifndef LR_TEST_CHECKSUM_H #define LR_TEST_CHECKSUM_H #include Suite *checksum_suite(void); #endif librepo-1.12.1/tests/test_data/000077500000000000000000000000001372027637700163605ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/key.pub000066400000000000000000000033071372027637700176630ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.12 (GNU/Linux) mQENBFBXDKwBCADA5jpCwpb/JKOG8mcFyIanNojDwpHwKoyjGNpZNPNUDJguvkRa IO3NdoyXYd5QVTOsnyKBaRaiLLJWI/VJxTOT3fwOPprrzUlkHwoWl+sYuSdXHASu m4lkBiXHsa5oiXPdrY6hoh5vsF8ASwCHXOwpR9yyvGEaUUMBl2GpJAX/cGVcL4Dy Z0pyJMLO4qrIPoX+wd1ZSFSc8JcAC4UtA82HCGTmesgialpwKdoQyt+em94oIM1f D6v7zzcRX/zLKKEzpFnU458WBA+JACkde3ohX//0fDCeaLqMzs++FCgwm/HMCszw RnINr+K8ENfMYBoeM7a7tnhiae+rkxWmvWz/ABEBAAG0LFRvbWFzIE1sY29jaCAo dGVzdCBrZXkpIDx0bWxjb2NoQHJlZGhhdC5jb20+iQE4BBMBAgAiBQJQVwysAhsD BgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRBGr5WKIvLE6fdyB/9OzDczaqGy 1rzk7Wp2C9S5QatFUFNWt6FIFPITbixT4jrDo/LyUJVWLw4ng7ldg79vmrzhpP8h yFVvuvGvSEMn5sgnZ83SEd4vRJ2O8K5RuVs5Kcj7ayLlxPpqbYOYmrmTaLwYTwdv 0wDnNU9IkkMSK752RQes4J+4XGikd8CNm5lw8cRQ7bcQd8s2rnCoiyGt7PIdl13z 8hO9KA52iUP06AbbIusbQ1jzsViEny+xQH7SZ53Ga4eRr0mW2iA20Mkp4Ieb+dNo 47Q8aHUqI9O4HTF/3Fzt7KmNxXCpCOhxTWx0IkqPGoZ0W63Aut/CVh0LXsBF2TUD Ym9P/IjRgJLhuQENBFBXDKwBCACvhlMcgjLJ4PtMTtauF1OXVTfODQSHo+qwKt4S GyDlTGayQ76pOqYkkzIRqmNYl1ThmcfzpmJ3O+hFQQ7OdguYcfkbfgIMjEJEbKG3 wsR5pm9zjwStzYHedwkct1nyROgBz70o16FfdiWOguw58jQZOSO/I2S3JpLsLgI8 KqdIk/0WuoOfzt+KcvL52lX94O2hBpRI0v6lDgSm7KkPGQrVFnSIUR5r73ceageL 5LmGm1TlEjWHwA9iYIvBcjnE26/l8u58IYQ/sUmn0u4jBcBNc7iqdWvlSLZLlMmi qnzDNhUup9neKGxgr4hGAblxiSxXlmOoFv0jEW81b4VximSJABEBAAGJAR8EGAEC AAkFAlBXDKwCGwwACgkQRq+ViiLyxOnf6Qf+PIG//12qp3hXZsvB7JQuQ4nUNwp6 Ufm6W9pFm3DOqnI9H9ZNzGbkoS5WwRp0B1NLfNKipQVORnDs6qve298ReRrmLKnk BPZqxFpPqLQ6X83Or2bqKiJS1axonIgqkImFLfxxqKoukvhn328Z2FVlrvkKSMU8 eHi/iDF/TCHoPE9WtnVSzsNU9i+9j8j//GO+bMC5AGNOxcBKlChFpLYpE/pfITL/ icS7wB9MrMLNvjlN1EKszQFxJrFVBGTt8hUqRH3CCUFRwbpE1QJ1WAzJ0Vzk5nWR rVZQiiLe03B8hC7/qRiB4bya5nbWcwe9ltPFja4/tTe92ivScFfCLyALVQ== =G300 -----END PGP PUBLIC KEY BLOCK----- librepo-1.12.1/tests/test_data/metalink.xml000066400000000000000000000020131372027637700207020ustar00rootroot00000000000000 1347459931 2621 f76409f67a84bcd516131d5cc98e57e1 75125e73304c21945257d9041a908d0d01d2ca16 bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0 http://127.0.0.1:5000/yum/static/01/repodata/repomd.xml librepo-1.12.1/tests/test_data/metalinks/000077500000000000000000000000001372027637700203475ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/metalinks/metalink_bad_01000066400000000000000000000026261372027637700232120ustar00rootroot00000000000000 foo bar 20b6d77930574ae541108e8e7987ad3f 0076c44aabd352da878d5c4d794901ac87f66afac869488f6a4ef166de018cdf 884dc465da67fee8fe3f11dab321a99d9a13b22ce97f84ceff210e82b6b1a8c635ccd196add1dd738807686714c3a0a048897e2d0650bc05302b3ee26de521fd http://mirror.pnl.gov/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml librepo-1.12.1/tests/test_data/metalinks/metalink_bad_02000066400000000000000000000020411372027637700232020ustar00rootroot00000000000000 20b6d77930574ae541108e8e7987ad3f http://mirror.pnl.gov/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml librepo-1.12.1/tests/test_data/metalinks/metalink_good_01000066400000000000000000000451771372027637700234240ustar00rootroot00000000000000 1337942396 4309 20b6d77930574ae541108e8e7987ad3f 4a5ae1831a567b58e2e0f0de1529ca199d1d8319 0076c44aabd352da878d5c4d794901ac87f66afac869488f6a4ef166de018cdf 884dc465da67fee8fe3f11dab321a99d9a13b22ce97f84ceff210e82b6b1a8c635ccd196add1dd738807686714c3a0a048897e2d0650bc05302b3ee26de521fd http://mirror.pnl.gov/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml http://ftp.linux.ncsu.edu/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://ftp.linux.ncsu.edu/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.symnds.com/distributions/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.symnds.com/distributions/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirror.symnds.com/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml http://linux.mirrors.es.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://linux.mirrors.es.net/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://linux.mirrors.es.net/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://chi-10g-1-mirror.fastsoft.net/pub/linux/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.metrocast.net/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.ancl.hawaii.edu/linux/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://chi-10g-1-mirror.fastsoft.net/pub/linux/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://chi-10g-1-mirror.fastsoft.net/pub/linux/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://ftp.usf.edu/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://ftp.usf.edu/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirrors.servercentral.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirrors.servercentral.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirrors.solfo.com/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml http://fedora.mirror.lstn.net/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirror.lstn.net/fedora-enchilada/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.lstn.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.nexcess.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirrors.kernel.org/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirrors.kernel.org/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirrors.kernel.org/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.itc.virginia.edu/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://ftp.uci.edu/mirrors/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.steadfast.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.steadfast.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirror.steadfast.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.uoregon.edu/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.uoregon.edu/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirror.uoregon.edu/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://ftp.osuosl.org/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://fedora.osuosl.org/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://fedora.mirrors.tds.net/pub/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://fedora.mirrors.tds.net/pub/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://fedora.mirrors.tds.net/fedora-enchilada/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.cogentco.com/pub/linux/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.cogentco.com/pub/linux/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirror.cogentco.com/fedora-enchilada/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.web-ster.com/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.web-ster.com/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirror.web-ster.com/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.chpc.utah.edu/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirror.chpc.utah.edu/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.cs.princeton.edu/pub/mirrors/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.anl.gov/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.anl.gov/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirror.anl.gov/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirrors.reflected.net/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirrors.reflected.net/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirrors.reflected.net/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirrors.dmacc.net/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://ftp.ussg.iu.edu/pub/linux/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://ftp.ussg.iu.edu/linux/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirror.umd.edu/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.umd.edu/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.umd.edu/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.us.as6453.net/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.hmc.edu/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://kdeforge.unl.edu/mirrors/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://www.gtlib.gatech.edu/pub/fedora.redhat/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://ftp.gtlib.gatech.edu/pub/fedora.redhat/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://rsync.gtlib.gatech.edu/fedora-enchilada/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirrors.med.harvard.edu/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.seas.harvard.edu/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.cc.vt.edu/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.cc.vt.edu/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.liberty.edu/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.utexas.edu/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.utexas.edu/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirror.utexas.edu/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://ftp.cse.buffalo.edu/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://ftp.cse.buffalo.edu/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.hiwaay.net/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.hiwaay.net/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirror.hiwaay.net/fedora-linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://archive.linux.duke.edu/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://archive.linux.duke.edu/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://archive.linux.duke.edu/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.nyi.net/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirrors.tummy.com/pub/fedora.redhat.com/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirrors.tummy.com/pub/fedora.redhat.com/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirrors.tummy.com/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://ftp.wallawalla.edu/pub/mirrors/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://dl.fedoraproject.org/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://fedora.mirror.nexicom.net/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.nexicom.net/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirror.nexicom.net/Fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml http://less.cogeco.net/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://ftp.cogeco.net/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://fedora.mirror.iweb.ca/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://fedora.mirror.iweb.ca/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://fedora.mirror.iweb.ca/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.its.sfu.ca/mirror/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.its.dal.ca/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.its.dal.ca/pub/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.csclub.uwaterloo.ca/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirror.csclub.uwaterloo.ca/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml rsync://mirror.csclub.uwaterloo.ca/fedora-enchilada/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirrors.ucr.ac.cr/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml librepo-1.12.1/tests/test_data/metalinks/metalink_good_02000066400000000000000000000035151372027637700234130ustar00rootroot00000000000000 http://mirror.pnl.gov/fedora/linux/releases/17/Everything/x86_64/os/repodata/foofile http://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/foofile ftp://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/foofile http://mirror.pnl.gov/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirror.pnl.gov/fedora/linux/releases/17/Everything/x86_64/os/repodata/barfile http://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/barfile ftp://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/barfile librepo-1.12.1/tests/test_data/metalinks/metalink_good_03000066400000000000000000000002551372027637700234120ustar00rootroot00000000000000 librepo-1.12.1/tests/test_data/metalinks/metalink_really_bad_01000066400000000000000000000033531372027637700245600ustar00rootroot00000000000000 20b6d77930574ae541108e8e7987ad3f http://mirror.pnl.gov/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml 20b6d77930574ae541108e8e7987ad3f http://mirror.pnl.gov/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml http://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml ftp://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml librepo-1.12.1/tests/test_data/metalinks/metalink_really_bad_02000066400000000000000000000006371372027637700245630ustar00rootroot00000000000000 20b6d77930574ae541108e8e7987ad3f http://mirror.pnl.gov/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml librepo-1.12.1/tests/test_data/metalinks/metalink_really_bad_03000066400000000000000000000004351372027637700245600ustar00rootroot00000000000000 librepo-1.12.1/tests/test_data/metalinks/metalink_with_alternates000066400000000000000000000742771372027637700253740ustar00rootroot00000000000000 1381706941 4761 0ffcd7798421c9a6760f3e4202cc4675 d28e40ca29b3b1d1e2976610e8858f976b06f02e d4f9ad66f7c6e000d8ebf9ec92ad2c4636547853708554d93dab672bdfd98ca1 c076ce639b0cbac02dc24dc3e32b04fbb834ffd7de6154a23cf1b61951e3b7af1eeed541cdff08410104268f799a1cae2365d17dce4bf1f080322d9d7e66a687 1381706941 4761 0c5b64d395d5364633df7c8e97a07fd6 522eca490cfc6728f2c8c5b80334c81f802c5fd1 099ff51a491eba3f16646efb5d16319249e0bb10f1dc1476d47e23b25e0e0f94 bd9d340176996eacb4b27e16557d777fa5db5b9e428c3c2edcbe3452a5164cd42a56addc3634f953fffab187684497960c1c70f4750906f1fefd33de0e28426a ftp://ftp.mirrorservice.org/sites/dl.fedoraproject.org/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://www.mirrorservice.org/sites/dl.fedoraproject.org/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://rsync.mirrorservice.org/dl.fedoraproject.org/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://mirror.sov.uk.goscomb.net/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.sov.uk.goscomb.net/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://mirror.bytemark.co.uk/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.bytemark.co.uk/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://mirror.bytemark.co.uk/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.ox.ac.uk/sites/download.fedora.redhat.com/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.nl.leaseweb.net/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://mirrors.n-ix.net/fedora/updates/19/x86_64/repodata/repomd.xml http://mirrors.n-ix.net/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://mirrors.n-ix.net/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.switch.ch/ftp/mirror/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://mirror.switch.ch/mirror/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://mirror.1000mbps.com/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.1000mbps.com/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://mirror.1000mbps.com/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.funet.fi/pub/mirrors/fedora.redhat.com/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://rsync.nic.funet.fi/ftp/pub/mirrors/fedora.redhat.com/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://www.nic.funet.fi/pub/mirrors/fedora.redhat.com/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://ftp.linux.cz/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://ftp.fi.muni.cz/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.fi.muni.cz/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.linux.cz/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.fi.muni.cz/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.linux.cz/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://fedora.tu-chemnitz.de/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://fedora.tu-chemnitz.de/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp-stud.hs-esslingen.de/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://ftp-stud.hs-esslingen.de/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp-stud.hs-esslingen.de/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror1.atrpms.net/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.linux.org.tr/fedora/updates/19/x86_64/repodata/repomd.xml ftp://ftp.linux.org.tr/fedora/updates/19/x86_64/repodata/repomd.xml rsync://ftp.linux.org.tr/fedora-linux/updates/19/x86_64/repodata/repomd.xml rsync://ftp.halifax.rwth-aachen.de/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.halifax.rwth-aachen.de/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.halifax.rwth-aachen.de/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.uv.es/mirror/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://mirror.uv.es/fedora/updates/19/x86_64/repodata/repomd.xml ftp://mirror.uv.es/mirror/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror2.hs-esslingen.de/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.heanet.ie/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://ftp.heanet.ie/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.heanet.ie/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.upjs.sk/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.upjs.sk/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://ftp.upjs.sk/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.acc.umu.se/mirror/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://ftp.acc.umu.se/mirror/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.uni-kl.de/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.uni-kl.de/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://fedora.mirror.root.lu/updates/19/x86_64/repodata/repomd.xml ftp://mirror.root.lu/fedora/updates/19/x86_64/repodata/repomd.xml rsync://mirror.root.lu/fedora/updates/19/x86_64/repodata/repomd.xml http://ftp.byfly.by/pub/fedoraproject.org/linux/updates/19/x86_64/repodata/repomd.xml rsync://ftp.byfly.by/fedora-linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.byfly.by/pub/fedoraproject.org/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.arlug.ro/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://mirror.arlug.ro/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://mirror.arlug.ro/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://mirror.yandex.ru/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.yandex.ru/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://mirror.yandex.ru/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://fr2.rpmfind.net/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://fr2.rpmfind.net/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://fr2.rpmfind.net/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://mirror.muntinternet.net/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.muntinternet.net/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://mirror.muntinternet.net/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.nluug.nl/pub/os/Linux/distr/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://ftp.nluug.nl/Fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.nluug.nl/pub/os/Linux/distr/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://mirror.slu.cz/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.slu.cz/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://mirror.slu.cz/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.tudelft.nl/download.fedora.redhat.com/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.tudelft.nl/pub/Linux/download.fedora.redhat.com/linux/updates/19/x86_64/repodata/repomd.xml ftp://nl.mirror.eurid.eu/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://nl.mirror.eurid.eu/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.astral.ro/mirrors/fedora/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.astral.ro/mirrors/fedora/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror2.atrpms.net/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.uninett.no/pub/linux/Fedora/updates/19/x86_64/repodata/repomd.xml rsync://fedora.mirrors.ovh.net/download.fedora.redhat.com/linux/updates/19/x86_64/repodata/repomd.xml ftp://fedora.mirrors.ovh.net/download.fedora.redhat.com/linux/updates/19/x86_64/repodata/repomd.xml http://fedora.mirrors.ovh.net/linux/updates/19/x86_64/repodata/repomd.xml http://fedora.mirror.garr.it/mirrors/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://mirror.proserve.nl/fedora/updates/19/x86_64/repodata/repomd.xml http://mirror.proserve.nl/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://mirror.proserve.nl/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://fedora.uib.no/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://fedora.uib.no/pub/Linux/Distributions/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://ftp.cc.uoc.gr/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.cc.uoc.gr/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.cc.uoc.gr/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://mirror.netcologne.de/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.netcologne.de/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://mirror.netcologne.de/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.datacenter.by/pub/fedoraproject.org/linux/updates/19/x86_64/repodata/repomd.xml rsync://mirror.datacenter.by/fedora-linux/updates/19/x86_64/repodata/repomd.xml http://ftp.rhnet.is/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://ftp.rhnet.is/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.rhnet.is/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.man.poznan.pl/pub/linux/fedora/updates/19/x86_64/repodata/repomd.xml ftp://fedora.ip-connect.vn.ua/mirror/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://fedora.ip-connect.vn.ua/linux/updates/19/x86_64/repodata/repomd.xml rsync://fedora.ip-connect.vn.ua/fedora-enchilada/updates/19/x86_64/repodata/repomd.xml ftp://alviss.et.tudelft.nl/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://mirrors.ircam.fr/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirrors.ircam.fr/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://mirrors.ircam.fr/fedora-linux/updates/19/x86_64/repodata/repomd.xml ftp://mirror.karneval.cz/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://mirror.karneval.cz/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.karneval.cz/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.pbone.net/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://ftp.pbone.net/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.pbone.net/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.de.leaseweb.net/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.ntua.gr/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.ntua.gr/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://vesta.informatik.rwth-aachen.de/pub/Linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://vesta.informatik.rwth-aachen.de/ftp/pub/Linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.klid.dk/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.klid.dk/ftp/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.free.fr/mirrors/fedora.redhat.com/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.free.fr/mirrors/fedora.redhat.com/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.digitalnova.at/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://www.fedora.is/fedora/updates/19/x86_64/repodata/repomd.xml ftp://ftp.fedora.is/pub/fedora/updates/19/x86_64/repodata/repomd.xml rsync://www.fedora.is/fedora/updates/19/x86_64/repodata/repomd.xml rsync://ftp.icm.edu.pl/pub/Linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.icm.edu.pl/pub/Linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.icm.edu.pl/pub/Linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.uni-bayreuth.de/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://rsync.uni-bayreuth.de/fedora-enchilada/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.uni-bayreuth.de/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://mirror.easyspeedy.com/fedora/updates/19/x86_64/repodata/repomd.xml http://mirror.easyspeedy.com/fedora/updates/19/x86_64/repodata/repomd.xml ftp://mirror.easyspeedy.com/fedora/updates/19/x86_64/repodata/repomd.xml ftp://mir01.syntis.net/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mir01.syntis.net/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://mir01.syntis.net/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://sunsite.mff.cuni.cz/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://sunsite.mff.cuni.cz/MIRRORS/fedora.redhat.com/linux/updates/19/x86_64/repodata/repomd.xml ftp://sunsite.mff.cuni.cz/MIRRORS/fedora.redhat.com/linux/updates/19/x86_64/repodata/repomd.xml http://ultra.linux.cz/MIRRORS/fedora.redhat.com/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.proxad.net/mirrors/fedora.redhat.com/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://fedora-mirror01.rbc.ru/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://fedora-mirror01.rbc.ru/fedora-enchilada/updates/19/x86_64/repodata/repomd.xml http://fedora-mirror01.rbc.ru/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://fedora.dcc.fc.up.pt/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.lip6.fr/ftp/pub/linux/distributions/fedora/updates/19/x86_64/repodata/repomd.xml rsync://ftp.lip6.fr/Fedora/updates/19/x86_64/repodata/repomd.xml ftp://ftp.lip6.fr/pub/linux/distributions/fedora/updates/19/x86_64/repodata/repomd.xml http://ftp.colocall.net/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.colocall.net/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.ciril.fr/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml rsync://ftp.ciril.fr/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.informatik.uni-frankfurt.de/fedora/updates/19/x86_64/repodata/repomd.xml rsync://ftp.informatik.uni-frankfurt.de/fedora/updates/19/x86_64/repodata/repomd.xml ftp://ftp.informatik.uni-frankfurt.de/pub/Mirrors/fedora/updates/19/x86_64/repodata/repomd.xml ftp://be.mirror.eurid.eu/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://be.mirror.eurid.eu/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://fedora.intergenia.de/updates/19/x86_64/repodata/repomd.xml ftp://gd.tuwien.ac.at/opsys/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://gd.tuwien.ac.at/opsys/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.wsisiz.edu.pl/pub/linux/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.udl.es/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.udl.es/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://fedora.aau.at/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.linux.edu.lv/mirrors/ftp.redhat.com/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirrors.linux.edu.lv/ftp.redhat.com/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://ftp.cru.fr/pub/linux/fedora/updates/19/x86_64/repodata/repomd.xml http://ftp.uni-siegen.de/fedora/updates/19/x86_64/repodata/repomd.xml ftp://ftp.fsn.hu/pub/linux/distributions/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://ftp.freepark.org/pub/linux/distributions/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.fraunhofer.de/dl.fedoraproject.org/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://mirror.fraunhofer.de/dl.fedoraproject.org/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://fedora.linux.ee/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://fedora.linux.ee/pub/fedora/linux/updates/19/x86_64/repodata/repomd.xml http://mirror.pmf.kg.ac.rs/fedora/linux/updates/19/x86_64/repodata/repomd.xml ftp://mirror.pmf.kg.ac.rs/fedora/linux/updates/19/x86_64/repodata/repomd.xml librepo-1.12.1/tests/test_data/mirrorlist000066400000000000000000000001561372027637700205130ustar00rootroot00000000000000# lalalalalala http://127.0.0.1:5000/yum/static/01/ # lalala # xxx://127.0.0.1:5000/yum/static/01/ # coments librepo-1.12.1/tests/test_data/mirrorlists/000077500000000000000000000000001372027637700207515ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/mirrorlists/mirrorlist_01000066400000000000000000000006251372027637700234050ustar00rootroot00000000000000# repo = fedora-17 arch = i386 Using preferred netblock country = GB country = BE country = BG country = BY country = RU country = RO country = GR country = HU country = PT country = PL country = FR country = FI country = NL country = NO country = CH country = CZ country = SE country = DE country = TR country = IS country = IE country = UA http://foo.bar/fedora/linux/ ftp://ftp.bar.foo/Fedora/17/librepo-1.12.1/tests/test_data/mirrorlists/mirrorlist_02000066400000000000000000000006221372027637700234030ustar00rootroot00000000000000# repo = fedora-17 arch = i386 Using preferred netblock country = GB country = BE country = BG country = BY country = RU country = RO country = GR country = HU country = PT country = PL country = FR country = FI country = NL country = NO country = CH country = CZ country = SE country = DE country = TR country = IS country = IE country = UA #http://foo.bar/fedora/linux/ #ftp://ftp.bar.foo/Fedora/17/librepo-1.12.1/tests/test_data/mirrorlists/mirrorlist_03000066400000000000000000000000011372027637700233730ustar00rootroot00000000000000 librepo-1.12.1/tests/test_data/repo-big.repo000066400000000000000000000017401372027637700207550ustar00rootroot00000000000000[big-repo] name=Maxi repo - $basearch enabled=1 baseurl=http://foo1.org/pub/linux/$releasever/$basearch/os/ ftp://ftp.foo2/pub/linux/$releasever/$basearch/os/ https://foo3.org/pub/linux/ mirrorlist=http://foo1.org/mirrorlist metalink=https://foo1.org/metalink?repo=linux-$releasever&arch=$basearch mediaid=0 gpgkey=https://foo1.org/linux/foo_signing_key.pub https://foo2.org/linux/foo_signing_key.pub gpgcakey=https://foo1.org/linux/ca_key.pub exclude=package_1 package_2 include=package_a package_b fastestmirror=1 proxy=socks5://127.0.0.1:5000 proxy_username=proxyuser proxy_password=proxypass username=user password=pass gpgcheck=1 repo_gpgcheck=1 enablegroups=1 bandwidth=1M throttle=50% ip_resolve=ipv6 metadata_expire=5d cost=500 priority=10 sslcacert=file:///etc/ssl.cert sslverify=TRUE sslclientcert=file:///etc/client.cert sslclientkey=file:///etc/client.key deltarepobaseurl=http://deltarepomirror.org/ http://deltarepomirror2.org failovermethod=priority skip_if_unavailable=True librepo-1.12.1/tests/test_data/repo-minimal.repo000066400000000000000000000002571372027637700216440ustar00rootroot00000000000000[minimal-repo-1] name=Minimal repo 1 - $basearch baseurl=http://m1.com/linux/$basearch [minimal-repo-2] name=Minimal repo 2 - $basearch baseurl=http://m2.com/linux/$basearch librepo-1.12.1/tests/test_data/repo_yum_01/000077500000000000000000000000001372027637700205175ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_01/metalink.xml000066400000000000000000000020131372027637700230410ustar00rootroot00000000000000 1347459931 2621 f76409f67a84bcd516131d5cc98e57e1 75125e73304c21945257d9041a908d0d01d2ca16 bef5d33dc68f47adc7b31df448851b1e9e6bae27840f28700fff144881482a6a e40060c747895562e945a68967a04d1279e4bd8507413681f83c322479aa564027fdf3962c2d875089bfcb9317d3a623465f390dc1f4acef294711168b807af0 http://127.0.0.1:5000/yum/static/01/repodata/repomd.xml librepo-1.12.1/tests/test_data/repo_yum_01/repodata/000077500000000000000000000000001372027637700223165ustar00rootroot000000000000004034dcea76c94d3f7a9616779539a4ea8cac288f-filelists.sqlite.bz2000066400000000000000000000540571372027637700336560ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_01/repodataBZh51AY&SYltazw@P>8W}oyg>DVT| H*DIPV $ӹH(̀;qٱ@ɮv.gq!uPNcnD\YB AJaMhiM`i2fbii&##ISjidhL i'h'z1TjЀ&&OSh4Mɱ & 0'j 24MC@4 4 h4"""hi5<̚Sѐdi < I=LOM2e=5='6LM&3L43IyOSCL4b0b56'OԤLSDS2a= h  Ph44j@Bm=&ɚIlF2m&2lhсѤО3)Ɣ43SL4fidTJ@h `jmS i=@P!MC@h@{A:=ˣE!sHݮ;ÇW\A7F P*m:3Q ёj$Q(4/0hܩRK& '=F-WWAdD4-zM+eV[_VKĕ:Hb{&`4 [5P٭8j\{[+h1&cPY[T:ǟ3nnfj#:S(!2R$."?#v|}$>rdY>Z. ڕ qّ4\yz6:oCXpHXN4gݗ`lUTZF䆡QQiYg?歊]y}'goO*g IHImp|B=k|t_xs0#kzއ=Czk ؏y H3iHUEG 4Mo -|?a`uR\|J">nyN!ugq<:__KcE9\ebةL.p2y-|z}98C8L +:PXW, ‘8 ҏl.jC QY|b`ݑe!>uQ +lG֑- PW^I06ok6 !{ $I@Il&I/&gi"IcM-[qu2Zek:VNoW7;>^ 0yyz='O|qYƦ!~pEn~M p3>CF2GLQL3\iXUV|)ᬬe 1a8ZuAb帶/DݿMEU$S9?DNTngWoK|_U͟kj#?ɳڭs?U{aOe{o߬WksTz=2DKR;&&io?3%%}_o2N6's3xohgi k]..];4Us:!* 仺'aO]5 `,rb9MKߧ^AeTGf[c4k&auc8L9.χ]~N\_Ekc3=koWnv|ZIr%Lmm΀ggc&gنM+8[!E{#4Z(6_;8oeof-5̵;-UNַzDwXH!% eJ(00I'4RC7Ǐ{-ksUW[቙lfg~ZzIom8UVIt mLΏtc2<}LK3fg&gmdm|ENO'ֶwUW!m?cbfg?T32Wjxȏ ~6jfo#UP; ^f 5yU"?lDMٞgu{a~qt?zֶ9USo3)-]L L|m%ɶoi8LH%뻾#owWjwUqu^^J:1<<0W]X:qEWc{[[oU[}[jKK-'*CsX*TN۶>s6Ka1i|#Y3*L9+md=gyW8EeY-l/y;aG3#> o}L=~׿#,omUo3<<MoKK}I~moUl-oe7?4o['ݛpoi7ߋfϯpsy` oQ{WuvSL^dLi<=?{]$X5ۯIB{8ۯ}xAi>387KK@~gKlmI@ k?9ʯ٥@>fg*\Wg幩03Y]̉巆D]3k;ԙȗkoMqTNK` @]aGqo8uSn;&3{K"-Z#?Uk}lKf4 -IUiCXT6(6s]BGȠS#T9d":]MTzn]ݹo1vcvJ3L|O{zoe%@ӉoKם3t?R%K7 Ibd%6_R߷x{v|\-k%0e~-D=F{QA,SL;,1(AE` _u?6q6:Lhx^-{t!cƂ<!M.Gt<i6C$$#V\=d#vi4ɛ[e %c [$%[pbX2DcϬH""Fe#B%\kv6af%6&DBԁoo1KeCfm<&+LD˲9M #VsjI`ݩm,-KUQnYW,f*G Ĉ$Ddj%\eb1dnYw]cFa 15j@;v#b1F 2FHvZS.e7gKm_KC`7{3ѥ_oj,Km~OVX=)/; g3? L?{>SrHUl0Ȫf|6RRцhz:I1s^wZxWU0귻L73;g=Z'5^DV fbߓm/jx̥R{lba-Z^ 0ݻ.2gɖg~ 4Uj=|nxZCGK\pAm|sW AQC5pACkfq 4[gL#owLRܤO` y&{S$}I-mQ7a3ǶA.%lȁn&^Y3{FQ"v7^(9y){ xΫ[ U,eV\ͦ 4Zf kp˛{_ 釐I)"~ Ϊ^Sdбٽ&D"di qyB޽/%-Է%^ m%=26I> Ϙ4~O'Kn[G$}VX7m[hS~f /ə٨7뼋XE!y#s6|C{ʯ/K7!1./o=r_Y-@K"\ni7D ۨl6[5L[XڹUb-ǮmDվ9"Ifx, r *iK*TV#~rSE7KAcŽbn1mdP񪶵9*tZ!@@ٍT$P&κI A%X&|WݒxȈ"3%0 -nހ1#;ޭm0ݠDa<'Έ M5?vp2<=^"">%Z^G 33"-UhC2O-#I,Ka;24Ay%:d8$:[\g%T`Nfb@S8X^D Z <&uw|;Cotl߈ wi6$'\ff O$NC{Iu X6N.&edoy: :?5!w݄.n %I=(Hflz ~㹙)wr R j]%7.žjm3D o@"I͈$?JHJLΑ ir{=߶i?v'ڮn Wq6$w'8ᅨdu[D]_-gq&|7c32K %}g$O$O $I=xߞiliΈkOKz ډߙ4&CXm* }x[}dN M%ۥo|ߡ1g7& x=&ƺc췯&e3۶OKO? XNwSg_|;]W3=[W_.>im'%9րDv6wv$I*q, xqO,\N#Ip|>O uN6?\x<1_|&.); ..)G?^V_$9y~~_Ob+NiTk%)JR)J]JR.)JR.)JR R)JR)JR)JR)JR)JR)J^ƫaaaa/aaa3<B!B!B!B)R7ڢ!B!B!BR 8fLB!B!B!BT B!B!B!B)Rs7TB!B!B)R7!B,T$ښڪNa_`=cbc@5\&XNZ7JTM-B.V?mV׶mkm5ih/&FVkvLkʕJ}!m36c>^ 99zT١se%=fr^־mƬ7v% m/!/]*#XՏ4j5̬=R:$%fB}^>Uw2X}sBVYa^xn:7SCKͥ޷|s[fdfG#{)Z}nmtimGVT~?MI ~3e8bdѸGj33ϯR,2Jl7oZ4&$bt4o R2YH#װHRR75*DI'q<+1X;Ú5VvY)ǷDT'.3 E]kQ([--C75Q6꽩hPVvymFl^}q+Xͩ ';@m>Z45%;t>iQ|e&r *aV9JrIQEKlkeyBݠZJ4BjY+hD '`$-EvJns'M'IxϮ2-[=maLs͖y{;s{0/$ap@VUZ[lm\\\ZNman1j4rvK*s]R@;]&aV*OFCj4gV.,[pϹ+cE7ɡ`>>yMsʨ4m"hRXQ65jC¬z^鰐ɻ0FfLq–h-kղk'(O6z+q/Q ]ˬ||M֎Ţ%v=#rdvPΧ,-jc.uʱ*-z i'h_ga3ҏ紕N)`νSܯEU*%J(ʺX& z{yʸ$nfDgߧnRҪt om DuL$la< vLr4kJSV(bYmER%]]G"qɊZjcm4٧Ok`PhbO!A= "B_NZBV6՗Im 1&5(gm3434whj)j**\"X&*dXU&rX:"VѤJ~­:JەH9},[MdJ)g]["}Dx[Rvg @xкb `X"2nR۫dK,1`& 0a^akmcK.o4(Wg7d$ b HPB! $$h^g_N~S@ Teej*:?VuޅoK$d#~0>_[OBr=zp [)Kl]UI+=̫m-l46m@ S_;ᄉ()VV}l~ 9""vluHNC.EayG9:Jy,j V:YemKUf2D4gu[~8P@O+c EiDf@")h ̈́e8[[6[m+J& tj G֖Eۆ3/ @[mm[gŤ[e+VN "~c{y+%4Kc[[Sb$-7./g g6n{ռ NJKLzuWv_W~6C՛ig1+,!}ns?vL>k3ht,({=N.3u?R's^f4?a"vӷ\SGuAyܲ{%d7\nlmBWи7?q7Qߕt+?es᦬@>UB~{= u[oևe>6,Sd֛=BK\lb_}ou2;N|^<]yE~>+=Fqb0bN"ձn[10& w}7H,לߏcd6G|E 7jͷƄ[p|N񹈓Erx>֦w/tQ%ÿ8쥡 i[Gjz3.J ox\ |S:ֺn$y:lj ɼ%qƹ F-M.-)} #J泆i{7lweqd/eeN~GZˤč0r{v/7xihpLԹX jc~ŏelϧ}^o73UK/np<%>5&>}ۡSЭCd{/7\{{~d߯ܟX}gria9ebL+?j8-\_ڨ1o6-T1qG=|gR]0i'ŷ[{8b]TҦJ\nzjidO+xe{=a#w"/Mſc/@}ffOrvW{TXY1VZނ[&6j,$'îk85[ٹaq{i'(kT=qϽ|)oJ4 8tQ{,Wwۻ5Y=c*{~vw7K;=o^O$ئ~j;K?sþ2~zOow:? :Fw%RtT:qC^Pf쩐Yh"NWﹴ|Zwx{jŽ={Ad8a]v~|7$fٰ2Yle% ,SﵓwSf!aad"Ng(az`>၌WGÔ<%0#TE=_֏>B1yaGmk]}[,ECK:?41 v@ 禿U[ 7wTg&j˭Yda_:]235X` N2 ^nϯN|#N y~~eȌ!_g%X r- "Xk YZmK7>DTd<4ykJb[`;I!!.xZ4F5n6\-KEY-H$"X3 d%R %Ge<qq>%#1TA>qg:`4ˉV|=r:T`cH@׭᫂Չڞ,~"ązҘ~{f"%'v ?)DPAT*l$x 3 9qX_1A&7XV5d8#Hv {ɓe!3(IzYQ7[[amuXƪK%\UZ-B[rܖ+/F5IeE[4[3>YO==4uN1}?uWX؈.0 @PR뽜#[ +}NtOstؒ+5D n~bΫ-U,kN1<>;竢luT['Uj7լ)Rڿ .smqRL`%!2PD\mqN%J- @ 'K1ߎ3gw%{3! $lm3 {6aĞ|G߂/wƣќ`t[?YCt>Ԙ/WӣiOߖtK5a ll}c7g^<.1)s JIیzSɔYmu֦B .Tw:[^𳒯ц3AT)ʦUa{6E][Vbuz&ft,yvkEC~zQmKB^h! Av[b)e6ʉuކfr7Qܥj3We=6b^;\Wp{9_rVj=OۻOL)txHN;ҽĻf K ^禓ǵr\u@WٔӖu\^~ =^ ogct>ӑr ʊׅ=de {kLZYw/?/q˻>]=/6]|~s6[{Qm2g߽@lt۩:4!)dz2eYVnv4w;c1FiEzWsT28WR_^X\w.yna+QVf?U݄^p6 xN?safMܥoDz'Zf-;`.FcaR4=7cټ^Ȣƫu /m"`1u-eU,h+̧E. .Me꯯dvt [=HɫxTvc=˹aY`~]c08JҲ+٪0HCoZx曮N[ǼZlJHIγoߺ ׶~ݓ-m\#+?Τz.{4ܗ?cg!uS4;s,5tzD1f 4 A,Rݵi](wWG+Yakϝ܏ 9 ǽxfjzV:No{ڗL_;3^>ϛ֒I$ I@@C$AԒAC ;P!!@}y-/zi]Դ쥧i-:ӵ:c9851o6/]4]ԻE,qōE,x$F豨-;E,v;i-;hbZ,qΎubLjKNEYNi"5-:QcZtXŎ,H֋7VkzFctE:Q6,sJ,pvu"X炁^y0d$N#FU0tN0((ЖKO:ZŎoGBZuڸqJ4]lc{)կZ,o["X8>,qK-7hor7Ĉ,c,pō$喝X+YNrGB,prKQіiE,vRӣ;_~՞I^1 G8wqz]Y"b_o~.^.|_ROz.$ RD`XVT@]?AZ4h8+p@oB C@֟⎢`έ ߎyN  cH8< D]eG N=@Cy6`Bv, %7cQrt@ 6A`H$HH}nݍ i:ZNÒ OKkfKR!? H=)蔤D"F]}1*c@RF:S" p #t-x:VXD""&$)Y#)uy1G LHY8|qtY Dd*֔J2 ƪc"?VUR.v m4,' 6q^4i}|88*i"3 JE/.Y""~ HunQ\ Viz||/@&Nnp<2wa$M{Si$xKztr9 TԶ48( EjoxS=X8>K^ _c34A/fd jtϟCI$$@Pi8c1ȁ DnݛwqӌiJt8À@hʓ/p,xtc DD@JeQK38eZ)^-EI\∉@cJr˜w3\33r_G̕>Wy3ӪX*l+IFs')Nuez21 @($n 12Ɣ㾫ji1sVRI=g>J[K0߫*ҙ݇ ѝ'9gLxD!@f ؁(ATO()"]dd0"Cp321 D"A뮺Jwq*NRxya8ak^9 E{L 0SzP ZZ/!x$`zgNS"LťcKrʩI$>$Fpg./_ggY?cMjIhɔBCIo1N {dӀZK-5QX # @vYh~NJq7""$C0@ HW soFff  BAqe[)Ϧ{^!0OQ35b7K,[3Ā$"Ȳqܐ HnfdGrpJrpM1>:}=dA4*pM 5&'9֚Ӡ7&r$РӟtrR, Ddi:Y tAuݣf^q“>Kw%c}CmV l<'e3^5vdet\z9EWQ ڼ-#ǗH ȈtU|%m- d%$D3wܾ|UTorK,&C50$"S.=aA$n٪eyǸB\kC9c^Yթ$g쎖u:c(]7OrQ3ZuY>uy&= rM%'bRozBz6$HԒ{ it;q#ϟKҔӧ# v]/ _EbJO Re)HU( R*Rr8* ڢ5*  $Y$RHEs۳vpǧ_OON5,I& jI9t) ʑQ'DC6=ԚF\zܚ&*rJD婙C ". hPDI)Fvpٜ S&e)$H` :Pv4ʩmtwtrxȷעBlӯWjڰulDjt 0dd}-feӻNRxΕ`HmDEiƙh/8[!ODiw0=Bk;-fd̨@wh^iP"_߳'JRwa:7/O4s1foc>8-[؜MO9pES|t%(4(@ T 1DdAM-BeD2T4u5UmQ8%&' V12㎭:5jݳvRVVjٳM^Jye׹LRԹ5N؎R@8(L(DD8@@-Qc< $ C,-Uax6,.YjLLumlIr̈́ M&!  $f ce DF:ۿ})ZRӯ[VX/)唻ɥgVvek,I-6wI܈d/:}i|h0P*/,J[CF)9ug0Yu`pE&. ]&h`;VV +18beIjϳ&IPܲʷmE%(+@XFy cx RؙR$%3"C`}#`B4pG&jgD"23)["" 0 6C >yt ]B'V3y=ct_Zˠ-jR"Ń E-l6 #/T6A[P"[x=)KqNmtU 7Xd38lvL\$-"AVLPhb2dStڲ%&7nIT*mD{100xcf۾ nʿ~~Q*NJgBm {X+G:) īP " x-4\TY)֋stt7rS78l9BnLN OfdŹ*]Z'u/O ۚЛ4׫DUjLUI'$lMI1mURBMaHa&`.{VD_-ßn\ZL3,p౰;9_tb3䐌phGҊ{zMR{NhA0F)N[Tɏa)FD(8 F=pokMID߳YMRffjIֲgqjqXnf%Y!V %Z  =4=qϔ#*T4+zns7-,ܝeamd(-OZW9"dZM͈|`N >ħU9]eBInKh҃xc@ $#c,Zة$থ,1`rTLR*'JNdP{}qvٳW-K9Ռč;gmVB(x0CIҨ GBI6T7':6:7UN ĒhJsQ*dBIC);SD4 lm.f.Jީ$k-&Ybىbli,oM$9N1 i0ʲI ꭔtO cˤj!"kϜs9Vf-:نφn!įǩN򵵢N9mN53k˜*\vXq},QΘJ[aۅYSt0K Y$ 08`"&hP0k[jeH>EM(5?(ٯ>0k r`[¦}ҽryw`s‰x8T8X@buˍʔajv5tsqju95T3i9inZN;cWEiE4d CzkN޾8aa"EBhLXSPĐ11ǸD0 +`ԛ AIzyJс5akW%"66 oro=F"ō9ZJQr.39,uxBPl ` *" 3}UW8VThlj(,Y1@ŇBP=QlPJ JCTG{BeX3.1Ui9NۯDIW"Ae(ܵeRx6Mv jLԣ,*I!ݢ[U:YN'Ujx)j$0^Μ:qק8N֧adcZ~/kk#=RsRGjͦתK $& .CDĤ`q*6"/  h-i14M]J+Gz&ߨMU9Jf檶TKmZū(;vmO^Ď{O M8qwNVv1ӥP ཬRjr%7a1T'9ͳ`x`XT$ >!Հ́]T 6 pՊ q^zҤ쭠u޷tUp 7wT⭅' Bb"kHX`21F!"ȶ˚3c;IfB0/"+M5lӦ:lt<3ÉE%-HTՕ$!Ug"DI$0D X#EDwcøg o/_EjKR3WQdOXJ#$.:q:,w4}y0,GYqyb|]%ߥ8GY5P|rz{C|T$Kd$uT=|u ::++:k)JOٲ3Y,xDVҷ۔N[b.C\ʜ&x7i MZ=mD͕9\#=fb1Dz=YN$< vD<֥BJVȟ5ZS@f_}$m/Ж3P QџeXti -W3ŽTVV=XSzExX1%%9ˮp3F8 >TQt :G iO$lj@6ׯk9E15dXX:~U鄘</S\*J|X~ߎQ{PXu c+({ds!oL'?BC.֣RtvN/W$I(̺WO94G!]C \UNyIudR:<nn-ICdZzs(tMttEiF]4֩WMuyu\pHms3e)rs8ne73! T4L@J\s3nYs$LmmLBZs?5* ReImJD A qah )0HeRrm ih6-Bzdܡ 6m&bi&(IAm׃2&m mjI&,)4[ 10dM`8qa0{ґ2$L2@1M6Y!m@(mXdapa'D&m@, 5!mAI1&-pmbAIÀ) !m!M 7l BM 0!l4؇$l@MMa$[Ә )l4YT$h!6-9%!6B $ i7Bm 4-4Ka6B $iECm!`6)`8$)a$mb &Zh m6-1 pm@"6iC Lm.! D0- I6Sh b 6i8i mp$IA@-5)m$Ç &d(M44mlI0[a m[D |Q*[L$M6 Cmj! &p 6l0$)I6Bm8l0$im6ЇhBm[h%ۄ a6B$IdmiAmـ (I4!m`n)6M O0D0 &eP!$abM6.p p.4543ad62e4d86337cd1949346f9aec976b847b58-primary.xml.gz000066400000000000000000000016501372027637700323440ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_01/repodataW]6}ϯJ&!#ªlڧʁKp6MȐi }8>6$p+rm[sJ@f*򸥿|~}t`y-'fKKk*!K' o 2UJ!14<;#`b'!!5\:a݄[:+SW nn@rK?EQDʵ]dኲL[iMCt4\kp-3C\oXQ6VynK nDFr!J_IůPpIDnh.D&ӢOf&( DľɊS:|¼O1hg54h!7)Zg.Dc¸m1[`w:Z+JsI?ju:4ZKuGvt+^ErބeSrhE0,6r=XÈ&.ϟ=&gg/^i [4.$Q; _ +`!in?]sQ9oDO"9rp4|z}cJq9 735cd6294df08bdf28e2ba113915ca05a151118e-primary.sqlite.bz2000066400000000000000000000050531372027637700332020ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_01/repodataBZh51AY&SY?mxc ޵` .cCAR 1O"=S ɣ i43H44ziF@ѠM i(:zO(2 GU dC@ #F!i24@ 2@ #@hJz&TdFiښhڍ 0COS=@hh  @4Ah!44ѣC@ɐh 141 F MI SAOdG2"z'4d 442#1@BU8$c`#b05Aede~ SLwU4'+(ƅ)aB"R#d`323Ǔ6Mlm;QdL˾G#8RqgeO4nαP"4pYJ::}NMHc^Χ֚ʗ,7>k ER""""L.8abAB=DQ:[#ykIIo[]:tx11|y,$lɊOS1$L -nŷ!Mk6D$4¼QO^69 u"&y-]sWZ =ٱ.+HR(շLqD%/EΨDj V2k ]{g{_0|d]]ǎtϞbU뵝$FJ"L],*^=(!S:UeD!))j{E]<ʣ-g<̓T00I?4Xg\(y|qU89D(S c2 1k hIZpsg')* $$z%zHp1M:$2|gXPdg}Mp.vN3dʳInnnnj*AYK[Ǐ 7USou6bqPH );P#UVnu~dM6Fnv5LPNF&lz٠.9dn|PN5̼&s0XX8S]b8ї d1 d}fC{as0M~y8_gW#&.cS@<<:\StNhJ K+\d!]ϭ~ Qe梄k]im,]mC (e)JRZe|nlno Plfr) I*уe@5ыmt'Uwm-SQxiz6WݬkB("( Q>ǜDq܈>Q>E(ic n*pDAp"  uUUUUUUUUUUV_TٗR-UqH;oHҝbʍ9{ICgfB0R;w|rQװ,Bffa* GN+ Yc.b;VB8-#wJ=>eF)P6ttPl&MIXڸQm(`Ge8C 35_Ÿ/BLvՊ@q|*d?Cyϼ|a <7LtmJme5K)z%%ul#4x<;I>븽^J(C01*!7,CnRdd\'JDDʕK8k 3VqY\ᎩA4$_G SiYxe˺_N&$r^/P B`#-t`v o1gzU OvJ_,# ]n,Dˀ.أFSS2*nӲ_^D᫾˗:öV&R$pu{]VH$pmI[רWoc[A[Q$8`N2]TQcoWo{{0]u[mu M5f31KIom2VY;K6-FH3"ĥ&I311Ffe)IDCh+FiD#$,@BDH a8977cdaa0b14321d9acfab81ce8a85e869eee32-other.xml.gz000066400000000000000000000014471372027637700324730ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_01/repodataoo6STh?E˩v-d|e/; mHHՇSF<"@ts\G_ߢ x*M{C"&(t@/BT:G0f':d69yCt &5EKwpjozE>i™%(Uʋb9ϲ,kH'g49,87׼ V[ CFEs=;qgY,*N8Z3Wk闍]| q'zwlHE5!* Q+ ϓW1`x{PjL7C;-nΔQw{K9xP!9 nCk,:rj#m^.=GQ ej>K@t/'٥nPA*K>Q JYDBct aBtdW53=zyF]fy(#R;iU}?ק_`c?^r7y%C%×W|]|%ݗK=h+Cquy/-|_d4-}7֧osw/TWKNg u=v7q>ueg-_2~}Os %\} ~Isg_qۧ}×| sK^N?؟ߗ-/0l,K/Y/q^V9_%|< _Q_]!}?Z>/|~ϧ&$gy/?_Z ˟Lj&M$h"~DSD M$пD4@/_$K"=^ 09="53?V~Tn) UN}=MsHU~G+?aa=Oח-}w^Kv>>G/onQꇥzg%ȏ/xϟ=_ӿ67P,G3Z]U*kU/> \./qEA; >/,h}|q#U:!NtJSL ~k[{>|󅿟4EY/h ^v-v-&sXv*,OVvq__rXv(ضۂ'/}}A,/eX඀x:\Bm-~_5ح}}<~4 >~2O]G+׺/X Uݚgr.ěmθwowSƻ,u_UA;`Qa6*WrX{jx,Ghr_zgxXfcWV??( }GXU\L}yCJbGƼV51~W{e}%~[W_>(J%(gx,HK[?./e^Q\Q;Wy~+%~gxo$޿W]Q-(gx̗E|;WuEIw{W-|x+JˮvۣvoM|֠dS<`OSPӠESqXOi)ojn}[Sok0TI$=_~P Ve|_e&wk&|WS=iS'm&庯/eئභ~龾 4xb7Xvl'*xTs}}8Vk?m.ܶ Wk(J'Zk`m{?X w5mlgx__~MEDzxA)utu˟|Par[PǰShmMOon탢 u c񿠿k?JJ YFg4:gt}秃H;no?YX֐Xw5 ~ǂOhYx,s+v+¤ߗֽ`ޝ$"{zx<4Lv躯  >ÖJ n%8P0lX:LK:ġAYCA}}aPe8vd|}uUy+G~/qӦ{C|zo{>뮂ۮ~}}A,M?*p׾x V{ Y Z໅}ro;>cO0߆pCcxX    gU/e`PY}}A,KcP8W͙CAw&6ts :uɻ/?x*_~A; 6 X_8#~ǂoh;x, U ? wB*xOӿE<xaHT@B<OC y3< ~./W X0yZ>k?-xg2ܙ #ǂsh3c~f<?(,6jce/?~kkYSSa=$VS74vE~_e91Ms,117FlB PFPثEI?Egq/TmFx#k3c?E ]%R=mǂ]))oZ%}}A,+~ޯnd) ewL@4+ToAwZl{_uXX ::\}Gv9Tp__ˀq,=b&X:>> G f#Xzlmq_o]ᗫ?E<'#z du,:^kF=~k%ޚE8_'ϚxLyke~}}A, ~gt \/p,IT< P,`< b៪ sz~Pq/lW^s g(>OK)vSRp]a)mXpR0Aqlφ.[/e?XfXEhXjbϴgZ,ijy v,/ Z & -Z C ljyR޽RuR]XYNllx?( !5 Inu|WCɻBht-h b!w lHY :R|[jD@BkȖo寸 o"K<̿ fMInˆs ?>aQPҨO ( ,yoE-nﭦSKx(8{XNTϥsIu}g}*NT8SyO幟? _bЕ!tu]BW!te]BWЕ!tu]BWЕ!tu]BWЕ!tu]BWU!$)!qBAl'8 q"@,'<) q?'viS6;Nic6;͎iS6;͎iS6;͎iS6;͎ic< {>.wdagՄ#"Ϫ" c0ehDVM$iDVM$ajDVM$!K;OL81;NL<1;OL81;OL81;OL81;OL81+_~W;ίw|E+_~W;w~E+_~W;_굜3PVȓzHBYABYo ƒƒƒƒƒƒW^X:( 90} 9(y r9(u 29(q 90m78p ǙS'w<8zq  #( P/A}zHBY"O!eeyC" " " " " " Bzm^jBCq5 !0PjBCa5!8PjBCa5!BӼ!tbB'Љ!trB'!tbB'!tbB'!tbB'!tRpc-#hP 3(~Ϡ=gP A30v9#gP Ay30nΜ,"쬚P zdYDY5@DUy Ƣ,"H Ҫ"4H Ԫ"LH ժdZ yqq}yuymyey]yUyMyE=1=rbzӣ'GNL=1=rbzȉ#'GOL=1=rbzȉ#'GBG!ttBGБ!ttBG!tdBG!tdBG!tdBGQ:UީzNգwSȝGT=r;UީzNգwS蝪GT=z;UީzNգwS蝪GT}ZFЬ@3gVLY33{fFϬw-~Zܵks箅]u}EYe4ZEPʰB N(,QYeZEPƪU eZEPƭU eZEPưU %$;ɴO8񴍓O8O8O8O8񴍓O8񴍓O8񴍓O8񴍓O8񴍓O8񴍓O8$)9IYiy䉤ę䩤乤ɤ٤ )9IYiyĉe@gЙsV>gϙsfB*AIǪ !vX%B%!v;|d A> YBb,ղT+jP?5ӧV̞S+yjOܩ;RfʜS+qjN75 (gbLΟ49&F BҠܞޜϽޚϝޘ}ܖ]ܔ=ܒܐ܎܌܊h98q390us98y98}:83!.T^:PjUց@mU*:PG%ԁi/L{tRڋ^݋lv=nۻ#lϖ{l ۻ+lﶰ=n ۳3lְ={n۳;l=n۳Cl={JeFs]E{U4E{M4D{EDs=Cs5Cs-Bs%BsAsAs 4e\~-?maZ|,?e!X|Ƨ;sjBegDVM1pj"J&дj"P&0j"V&%h;63h#;63h;63h;63h;63h;&E^%^\\ň^ȥ^ȅ^e^E^%^^]= }]=?˥3_~>̗_|>3_~>3_~>3_~>3_ 8|>}o 7|w%?w%?w%>w%?w%>w%?w%?w%>w%?w%>w%?w%>w%?w%>w%?w%>w%?w%>w%?w%>w%?L1br M &AL1bb+^!&CL1#bbK䞈M!t2A_ʕW/r-Ջ^ }rC_ W.zM^}E_0W-ilˇŶ|Il-öz )lˇ| l|kˇ|kϿ>?;EΟH D?U)1xPOdSe<(!xPO*A ?Uƃ*%x>3.w`frՄѳ"㪉"c0foDVM$!nDVM$lDVM$"#~*W}. r'OeSuYp' Oe˂+p?QW. N|hbVZ1P+hb(VŠW9*\`fbhUT9* /vȁUBOJ B_Agd Ad Ad A'd Agd Ad AڎBx*AIi !n?^%B܁%!Aw!g A ĝYB^,!%ZJ JJJJ J J J J J J Z2j%P?f S3zj%O੕;5cV ZS3rj%N75flhf8q6 3g0u6sg8y6g8}6g863hiCg!3zPBCg!;zPBCg!;zP+kAe̟V2}ZOi=eʝ*uZNi9eʛq2msW]-Z?w|jsW箖]Z>w~jsW箖]Z>wr_RUBOJWV!*Eb,!J%KB쿒%!_d A0Bj*AI !\%Bl%!af A0 vYBb;,!bN^ t !Juxwxwx wxwxwxwlC[p[omն[j[nmuvٖ[d[omז[^[osmv֖[X[o[mUԖ[R[6y_y"\ S'W.}ޏ#C:: C::#C:2#C:2#C:2#CHZR[oIm%[R[nIm%[R[oIm%Ԗ[R[oIm%Ԗ[R[oIm%Ԗ[R[oIm%bЕ!tu]BW!te]BWЕ!tu]BWЕ!tu]BWЕ!tu]BWU!4li1C!eE бf=$'ƒw~5ks]f>w~5ks箙]f>w~5ks"kAha-̟E0}Y=ga,ʝ(u΢Y9ga,ʛq0m^'.wdagՄ#"Ϫ" c0ehDVM$iDVM$ajDVM$!K̤7^Lzq3ͤ6^Lza3ͤ7^Lzq3ͤ7^Lzq3ͤ7^Lzq3ͤ7^LZfTS}n*M<4gTSy^Ki>,gTRyNI)IBɳBBBC3CSCsCɓCɳCCCD3DSDsDDɳDDDE3ESEgY:+ 393} 39+y r9+u 29+q ̸9+m ȍmM- ǭǍmMzJhC~BhhhhhhhC7:lUЃ!`ƒux*E!`YB!`YB!`YB!`YB!`YB!`YBbx@zR*<AV.ze A ^YB⎯,!qW+KB/vigUГ\Ϫ !N]!d As>YBO8%!d As?Y"߱fJT3jO>5fJS+wjNԩ:2fJS+ojMʹyrgNvVM(=,"H Ъ<cQVM$AiDVM$jDVM$jD^d=D &Ȑ5ǀz YY dʚH CVD&Ȑ5@2d}&ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&{N2`0z~eT`Tb(#######3zZ0dCg3zVϰT=Cw@ 3zR0D=C;FB#Cht !4:FBChd!4:FBChT5R{xeõ^6^{pe㵗 ^6^{xeõ^6\{xeõ^6\{xeõ^6\{xeõ-fu|'NP;wB: u|'NP;wB: u|'NP; D  PVxPV'=$      Yg`v-f`fmf`v'f `v7f`vG-f@l+ֺr[m+ֺb[m+ֺr[m+ֺr[m+ֺr[m+ֺJJJJJJJJJJJJJJJJJJJJJJJJ-v !48BChp !40BChp !40Рݮ+ubJݮ+vbݮ+uRݮ+uRݮ+uRݮ+u0O辘ysՉB"DU "Թ\UKsBY"p,\yP~<`(K@W1%+M2nJ)~nL1znJܹ1vnJC̹1rnJ禼1nnH\ZiPmC3z(6Y!6Y[hPmCmCmCmCmCmCmvPgx_̼i:UA:;3\%<;3Aώ D:,0K@;3AЎ D Ԁ}1CQU"OQUAB ]]ݮD ԰uvPnY"j:KB ]g@%> @yR-YD tUK t,'KB.ЁK|D t,'KBF@6@&@@@??????SO.wdagՄ#"Ϫ" c0ehDVM$iDVM$ajDVM$!w\gXwTwPgLwHgDw@wb pU`X0GU,7,"P znYDP[5@Uy ,"H ت"H ת"tH ֪2d . !2deM(=Ȑ5@<DCdʚH CVD&Ȑ5@2deM$!wd=D &Ȑ5ǀz YY dʚH CVD&Ȑ5@2d?qyY!+kB=D2deM1 "CVD&Ȑ5@2deM$!+k" Y3. !2deM(=Ȑ5@<DCdʚH CVD&Ȑ5@2deM$! LWj:*AY (6_%Nl:%&6g Mdz`ĦYB b,M4)&?gRL̞3)y&Ϥܙ;RgbLʜ3)q&Τ7fLEi0m͢Y6fa,LEi0m´Y6 fQ,LEi0m´Y6O;sjBegDVM1pj"J&дj"P&0j"V&Ȑ<Ȑ5@"CVD&Y!+k" YY dʚH CVD&Ȑ5@pyY]YE]YE]YE]Y]Y]Y]Y]Y]Y]YeWU`*r0|8 _"WU`*p0|9  _EWU`*p0|9  ]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]7]W<Ȑ5@"CVD&Y!+k" YY dʚH CVD&Ȑ5@,"~yY\EQ O?j"Y^EQyu,"_E,"_E,"_E,"_E,"_E,"_E. !2deM(=Ȑ5@<DCdʚH CVD&Ȑ5@2deM$!qyY!+kB=D2deM1 "CVD&Ȑ5@2deM$!+k" YpyY!+kB=D2deM1 "CVD&Ȑ5@2deM$!+k" Y=. !2deM(=Ȑ5@<DCdʚH CVD&Ȑ5@2deM$!뭸{~>'xyJ>'Ÿ^Μ,"쬚P zdYDY5@DUy Ƣ,"H Ҫ"4H Ԫ"LH ժd%Y˰JJĘJ̨JLhJ̥PJJ8Jʢ JJJʟIJIʜILOHOHOHOHOHOHOHOHOHOHOHO   º$º,ú4ú<ĺDĺLźTź\ݳU=@, OpO<@,,@,,@,,DCΜ,"쬚P zdYDY5@DUy Ƣ,"H Ҫ"4H Ԫ"LH ժ"dbȰUjdL5:FpM#it, ё42F0E#ht !6/viѪ'%Gƒh"qQ(KB\|%!.? e AKD h1vg1{vg)wvRg)svg)ovҦLNi1m:M6ҦctLNi1m:MǴ6ӦStLNi1m:MǴ6O߂eΜ,"쬚P zdYDY5@DUy Ƣ,"H Ҫ"4H Ԫ"LH ժ"dq3zGUSCgD3zGS`K=Cw$ q3zGQ C=C<B*AIiǪ !X%B%!n7}d A> čYB֏,e-#h3̟g+|̞=g3xʝ=cg3tʜ=#g3pʛ=f7mgc-;iƎ5vڮn7kWcǭ;qN4vܦ.7iGc-;qƎ3&['/?pÉ'/?p'.?p'.?p'.?pY2f%?gf 33zf%;3cgV Y33rf%73ff<>,wdagՄ#"Ϫ" c0ehDVM$iDVM$ajDVM$!M(=W:>M$!AyuW<>ȐG~^ȏ"C+Qdz#? Yx|G" . MYo"zA֛D&7d MYo"z@֛&7d MYo"z@֛8-|ԄSC>j" Yߺ{GQuUdCV![wYEo=duUdCV!kŎa58R#jp@ 48XJ#i` a48BR4{EKW-^4sK3W.^4{KW/\4{LW0\4{ ELW1\4{̅L3 PG($J@) ^N'@Ej PUe/,TxiBB Chq-!8ZB Chq- !0ZB Chq- !(섒tU]@WUse\>WfϕsU\@9 r !gUBOJYƒJu%!>ne A\YB#WЕ%!>ve@h#;6Nh; 6Nh;6Nh;6Nh;6Nh;6d-#RU S)~*O=RT JS1v*NЩ9#RT JyS1n*M ̛Z;.VE A j|@P5< A j|@PFb9?a8x;Ca8p7?rgNvVM(=,"H Ъ<cQVM$AiDVM$jDVM$jD 3UVȳzHSYAQYoƒƒƒƒƒƒh-#HG 5XVlxb`ņ+6X; hUГR+UABlJu (KBl%![e A V@YB:bЉ!trB'!tbB'Љ!trB'Љ!trB'Љ!trB'I!4oh1C!eE ٓ=IɓÖy[UBJ<ƒux˳Juxx˓%d §bTJS1x*Nة:CRTJS1p*MŸ6LZiS3mjM6ҦfLZiS3mjMʹ65ӦVLZiS3mjMʹ6e4Ji0mMôi6 Ӧa4Ji(mMi6Ҧa4Ji(mM -;p¹c N-;p¹c -;p¹S -;p¹S -;p¹S -;ps_̼٫cϪWӞUA:{Y%<{AϞ{Dg,'K@{AОM|D t:090{9(wR9(s9(oҦ-O[Lvy cq\"Ϊ GEğU DZ5`,"Ѫ"(H BӪ"@H Ԫ"XH C&ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&HӾ:鴯}ugfϙsV§a4F0xNi: CQ4F0pMøi6^rAPA\zAPA^rAPA-^rAPA-^rAPA-^rAPbPBCcu!9:PBCsu !1:PBCsu !p/v Ǫ'.ƒ؅c" G؅#KB‘%!vpd A]8D $!6wA =ox w?o}|w==oyiW6{^ig6{͞iW6{͞iW6{͞iW6{͞iWd-栴90mL9(mL栴90mJ栴90mJ栴90mJ栴e-ӦWLi+mzM6=ӦWL^i3mzMϴ6=ӦWL^i3mzqywqypq wpqoywoynywnymywmylywl b1h n1h n1h l1h l1h l14*F?#gT̞3*yFϨ;Rgdʜ3*qFΨ7Fs6Ks2 s.r*Njr&Kr" rqqKq qp'\"Ϊ GEğU DZ5`,"Ѫ"(H BӪ"@H Ԫ"XH B\O~>7_O|>%ķ_O~>E7_O~>e_O~>7_O~>bZBCha-!8ZB Chq- !0ZB Chq- cMB@(+AI=$ 7OzHBYABYABYABYABYABYABꖪꆪvfVF6&:0هYՖ&$~- LT[$0ASmI`A.lGK?Yϕ~S*LGJ?Qϓ|$&,GI?IAϑ|Lbm(QrwF%v8Jnq(Qr6G}%w:Jlu(QrvG-vB'!tbB'!trB'Љ!trB'Љ!trB'Љ!trBgY:+ 393} 39+y r9+u 29+q ̸9+m.isQ\6Eisa\6ͅisQ\6ͅisQ\6ͅisQ\6ͅisQ\e-檴2mLʴ*mL+檴2mJ+檴2mJ+檴2mJ+抴a|#{8:niy|ϣ{4^K>h%v;g ! $7X`M|5 7X`M~5 7X`M|5 7X`M|5 7X`M|5 7X`M|;=ӳN;ZӫNϾ;=ӳN;=[ӳN~;=ӳN^;=[Ӄi:pڳi=;pڳi:pګi:pګi:pګi:Iѓ'nHOޑ%=qOz]ғ'nLOޙ5=yozӓ'nPOޡE=yz&$U;v$/I^t'yN;w$/I\'q O"Uɋ|W$/I^瓸'yOR>ɋ}W$/I\'qO5?ɋ~W$/I\'qO!*AIUAB%-vB!t`B!tpBЁ!tpBЁ!tpBЁ!tpB!5"0 BU⯋ 5?!a!!4:FBCht!S'1a@n 3 2l0. 2=:0 82@#09:P p9:` Ȱ9:p 9: !a/vi '5̫ƒy"q s9KB\Ü%!a0g AkD Th1C!eE [ϖul@['ЖumB!42FBChd!42FB#Cht !42FB#CH;/vعb΋;.vعb΋;/vعb王;/vعb王;/vعb王;/vY//½//}/]/=//..삽.ꂝ.rumr'UInS)Y,)*ɮTC$Ѝ@&CC{yK5 4UY{mz׭~z~zz7zWzwzᗮzᷮz׮zY2f%?gf 33zf%;3cgV Y33rf%73fVZMPM`M`M`MPMPMPMPMPMPM`z_׃~]u=z_]u=z_]QRSTUVWXYZ[\]^_`abcdefg`&bdo"t&{Sxdo]!^%!^%!^%!^%!^%!^%!a# r0~> ga8v:f#a8n6u=z_ףG~]u=zȯ_#G]u=zȯ_#Gq5Gw5Gv5Gw5Gw5Gv5Gw5Gv5Gw5Gw5Gv5Gw5Gv5Gw5Gv5Gw5Gv5Gw5Gv5Gw5Gv5Gw5Gv5Kxgrt'zp*zl*zhgrd'r`r\rXgrT'rPrLx|n7;᭎t|.79-p|77پrgNv}*"4MO!P|cQAiz iz iz vY˰ڕT;cjgF +vPڕH;hW ])3vٕ<;cgW ]i+{W=^ٛ2wyeo畽+{WV^w{en+{W斯=_w}enʶ!B!B!B!B!B!B!B!B!B!B!Br3j:tәOg~C:uK̷ٯ3Sg|S:]u̷ٯ3W9?>??>?>??>?>?>?>?>?>?>q7w6w7v7v7 v   t(9?P̞s(yϡ9;R`ʜs(qΡ97˝9UDiBɪ4=BMEUD8jB)DhjB)DjB)DȲŎ p ` P @ 0 bН!twBw!tgBwН!twBwН!twBwН!twBw!d"]/].{ z@**@**@**Dz[ࠦ(BJ‹u88Iwx88J=T%U AUB!Mn"45)ASAS ĩUB*!qjt85JB]%!:VZB+Chu !:VZBChe!:VZBChU=ZFУyT<2}>̞GFϣyd<*u:ʜGFΣ瑁yd<2mrgNv}*"4MO!P|cQAiz iz iz d-IIĘzbF=)NO'CI8zR=1BO'IyR<1p6Ey]MQ>v7!p}"p}zJ>E_%wW ! ?fÀ]Y̾^>ª XU} \U} `U} dU} hU} lU} pm}ުD}UJؚ[ ֪ʨD]iMUʈD]gMUhD]eMUJH6Z&ؠ\SkPb LAI50&ԠtLRi`" JI4(&РS?T@TAE!4ZFШ@#gTLQ32{FFϨٓ=Iɓ>#s>2>#̳><#><#ȏ3?<#؏s?`})Aqx} ox}Amt}Akt}Ait}i/._]x|v o/._]x|v勷n/._]x|vY:@`s(|f9<PCs0te98PibY8#ga,8vY< gq, 0~Z@ #hq- ![b>} 3Ã} 3Ã} ~?~?~?~?~?~DBPUt/.UU BPUxPUxPUxPUxPUxPUxPWȏ`Q"?E~+Wׯȏ_Q߾"?}E}+WWȏ^Q߼"?yE}+>Zͳi̴yV<3m6Lgͳiyf<+m6Jgͳiyf<+m+kWǮo]tE~++WGo\Q"pE}y+Vǭo[Q"lE}؊6m4jhmQF#Fm6m4jhѨmFFn6m4jhѨmFM2m6ƴ٘6fclJi1m6ƴٔ6fSlLMi1m6ƴٔ6fcӷpECӦ"Dp$<n:x Nr9qt`;8z:頏㧃?9:9oB:#):З9MEB<#IwxFNxFNxFNxFNxFNxFNfZBChv !43fBChf!4;fBChf!!567EGRmn /Bb{s"ùJBq >*!ӹJBu{_B+Chu!:VZB+Chu !2VZB+Chu !22-vePvePfePfePvePvePvePvePvePvePve A AMQDԠ" AMK 4!JBD 4!JBD 4!JBD t( F5EGRWk /"rX]9JJBWЕĪD t(*]9JJBW-visSt/MEB$B\%!Nv';W A UBd*FA ]ox[ ?o~|[=]oz|[@nr/[ F  ]@nb[ ?na[3q3;MU>YQiz>GMS4=MMS4=SMS`4=Y3" E~'N(P;wB" E|'N(P;wB" E|'N(P X7&o`M5qk X7&n`M5yk X7&n`M5O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(O(ORWN^55zSWM^95zSWN^55zUSWN^55zUSWN^55zUSWN^55ze-fWLi+mvͮٙ6;fWL]i3mvδٕ6;fWL]i3mvͷ;s4]Uiz 3p4=JMS4=PMS04=VMSȐz}w!jz 3 E)dȪB)dȪB)dȪB?O2gUg1~dϪbϪ"$}V}V}V}V}V}VAȍ#En(r@4Ni"7 qȍDn$r 8F)"7iȍ#Dn r8>!+oECU 0)P)//ܩOSx@OSxOSxOSxPSx@PSx{0a9&sL81yc0a9&sL<1qc0a9&sL-v%Pr%Pb%Pb%Pr%Pr%Pr%Pr%Pr%Pr%Pr%D'NO8W?q~TġI3G'NO8O?i~4aF'NO8G?i~!2m36qclؤmb6&M#6qCl~Ia7&M 6q#l>mI`ϸ̛ϼ[ϼϼθ̛θ[θθ͸̛͸[͸,E[ў(BJ{‹uIwxJ=s_%W {=UB:ot_DӉ|V'N{;ov_Dۉ~v'NfυsQ\<΅sQ\:e΅sQ\8ͅqsabf5EGP?^ dM⿋!dUB*!pY~8J@FV% # -vi$YSt/dMEBL$BMV%!N'U Aʪ eUB┲*3ڍv[hD# v;4 ^hBڍv4A2m6f` Ji30m6fP LAi30m6f`ڈpp>} 3g8pp>} 3g8pp>} 3g8p`>} 3g8p`>Á} 3g8p`>Á} 4*F?#gT̞3*yFϨ;Rgdʜ3*qFΨ7#F}F9g~F=g䐟S~F9g~F9g~F9gF9g!n3gt->[|Fn3gd->[|Fn3gd3 C2+C: C: C: C: !|tQ8pbO] 'Nl,Y8pbo‰݅ 'Nn0a8pb&É] 'NZ1ykĭVLZ1qkVLZ1ykĭVLZ1ykĭVLZ1ykĭVLZ1ykĭjL1qjL1yĹjL1qjL1qjL1qjL1qƋe(^@/̟ Eya(y^t =]|sO{Ӆ.>tṧ=]xsO{Ӆ.>tṧ=]x)e6wA =ox w?o}|w==oytbo܋#x7Fv2wdoܓ)#pWF}3wfoܛ9#pwF]= }]=%v>z|ѣ#gG>=~>z| #gGB9>z} IgG!=oz_D~'uO>o|"_D|'O{?o~"_D|s1n1{n)wnRй)sn)on?;s4]Uiz 3p4=JMS4=PMS04=VMSHd-*)c*1*1)1C))()()#()')yc')s'1m~Ν2χ}2bчQCAOeU_1ՇCW+F0P#}Aw୪nMW>^UښbjMUʘD]hMUxD]fMUXD]d/SG-+e~hv_($—r ~h_(\$BYf'2/?IPIO/O_~N~:??N~ {)D?N<:AD?cӻڝEt/;NREk'Q{A^lvҲlEb' {ǽo .wUi ч"\"5Mg0Uaiz Кiz Ěiz !;|oֱ-ؑfc[W\-iul|ձ]UdžR(Aul|D&:|vF;wuXkap۹ݮo熻ǝ[:{f3Ϟxg6=왍g첖+v?;gW̞ѳ+yvϮ;Rggʜ+qvή7&ZMVdMfdMfdMfdMVdMVdMVdMVdMVdMVdMfHqT̩SgN5Ξj98{qT̩S3gO5Μj=8sqT̩S3gO5ΜjR9rk/3,gZ|r9r˙/0gb~ǜ9-sk̙/4gjǿ:^BW+C!zu:^BWЫC!ze:^BWЫCU!t3;3{+wRй+s+o;_;s4]Uiz 3p4=JMS4=PMS04=VMSHUd-ê( c000 C( ( ( #( ç(y c(s (m -f޼K8UQD>ԻDS^DS'KUE?UE@UEAUEBuxm_}upm_}uxm_}uxm_}uxm_}uxסm_;a܆kM;`ڂsƮ ;_~k^zk΍]vkM\rk0b8#gbL39v&<grL 31~&@#hrM !A 40Ϡ>g` A30x:CgP A30p6 -v, ʼn8rfY:cgq, ɳ0zgY> gq, 0g!jn")AA WUB*!UsJB|\%!x[č(B_JC7‹8wIw8zJBQ%!ߨpT A#8 !U"e-#hT 3*~FϨ=#gT Q32vF9##gT Qy32nFU2mJ+ʴ*mLʴ2mJ+檴2mJ+檴2mJ+檴2mJy\u~ZuXwuVWuT7u~Ru~Pt~Nt~Lt~Jt~Hwt~F(5Yj䧩ɏS&?PM|H5jC䧪U&>XM~h5j䧫W&>`M!MQk4!qF"qF8eJBQ%!NڨgmT A6D Q;'s1k;'s;sѪ;'sꮱ;̴y33pf'ș9Cgf̎ٹ33xf'虝=3gv̌33f'ABHb9?a8x;Ca8p7>KO鳿>k鳿>O3>3>O3>,볿> O3>+쳿>KO>k>ONh;wB -Zx'Nh;wB  -Z|'Nh;wB  - i6i6Qi6Qi6i6i6i6i6i6i6i6jjjjjjjjjjjjjjjjjjjjjjj^y敯Wj^yկWj^y敯Wj^y敯Wj^y敯Wj^y敯%280<.<,<*̓8(s8&S8$38"8 88lLMi1m6ͦ٘6fclLMi1m6ƴٔ6fSlLMi1m6ƴ٘6rkX6>`m~kX6?`m|kX$P9 ճP= P=sP=SP93P9P9O9O9ԳO9ԓO94s(2ϡ>"s(ϡ<"s(2ϡ>"s(2ϡ>"s(2ϡ>"s(2ϡ}u !:VZBChu !2VZB+Chu !2VWWWWWWWWXX#X3XCXSXcXsXXXXXXX]4q h=E1wѸsݓ4q,i9MqwӸ{Ɲ5qLj9Uw(T@T?ES>ES=SmY37gaž=s{Y37g]-v<0p8f΃C灩yp<0x<gyp<0@gЃB&reȕu"W\'reȕy"W։\'reȕu"W\Y'reȕu"W\Y'reȕu"W\Y'reȕu"W~˝9UDiBɪ4=BMEUD8jB)DhjB)DjB)$Zi1]""%"<]"p /"` /"P /"@ /"0 /" /" O!.UEGRPUxPU_ .UUUUUUAaì1c3f9s50saì1cf9s90kaì1cf9sӷ?+.wTai '"O" 5Mg0Uiz iz aiz  딵 SIu2Nfԩ:Nd. St2Ne :B'#T Ss2vNe96 wCY>wCW Ƨ2ԇCW+FP#}>T}ŌW#M<]D,5BQE ?(?`AeQE ?(4Px m²JMQX ‹*555h5H5(5 4(?gP ̞3(yϠ;Rg` ʜ3(qΠ7FvWϕse\f)Y~#Bbͪ]MQD>Ԫ]ME:+w5.]U"YJ>+wUg*U% tBK-~rK-'~rK-~jK-~jK-~jK-r|wʧ)~|w')~|wʧ)||wʧ)||wʧ)||w# 3PEK=)@ MK Ȇ*=JBl(# D Ȇ*=JBuz#ɍ\7rur#\'7ruz#ɍ\7ruz#ɍ\7ruz#ɍ\7ruz#ɍ\'Ov5j>|TWɮS]'Ov5j>|dW]ͧOv5j>|dW?DsP9~~!JY/G!G-/|)ZB/_(Kx e ~,a/%B_HPV܅v=b^(ׅud]aWzhՅ>uH](rz}w!jz 3 E)dȪB)dȪB)dȪB7j:XIi,/i_Y]HY}d .p q]t[zpE. ]=V0C.8p I=42(ku<ے_gS$7[~܎aO O?[a%Of^c_!~77ah}op7 fd96942c919628895187778633001cff61e872b8-other.sqlite.bz2000066400000000000000000000025771372027637700323040ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_01/repodataBZh51AY&SYJ'kTP!BP`{aCTF0IM҇I2iF M4hbi2A*F2OM#DIi2 4AɀF24`&0 Fa12`&Ѧ##L Ld`#р(5=#Hm' = i  m1*Ow5H !3? ;QQMUZ!ؕaWc>~+PBQ8`~ujq[k)˫ȋEEk%FQsyR:N2A;0 RNb"ҨukKjRAkK.үF_cްXpWzi"~ +F3+=6]z!3`6JIg 4dqfLYecA(2n͙U;c|$DW9nIS`02(aRa.ux(~8J VvSLI̪\7*5 Ah0ِ v0#4k*GV"Q)œU;&|F,J/H:SIaZXE7وgqD. y[Zna'G˫um .fT^u%db c=DK$TH@Y1~ZtV5"&vuɂr+P ciK d, @xޮ(rN)cgH'!b=Al[/Ñɕ\dz0ؒb\An' ^1upSDVMEuRdl窳LtUQ4MNkL{YQ$k'uD"Ơa5d@qB q !U [NAOL&jY"[( mY321@BS}!# ç{(+
(q LB̎9ʔ́nʊpl@bBW )ET.5 $J5b^[d[tY@a+L V!U%2'R:Q;cKAʥb &t%7.g"V65H!KX,?40/' ߝ&D*oٔª%yg d*]|+&b!šgP"X)Bhq R%^ ٢y|n82FmȜ&erC@4c.p!librepo-1.12.1/tests/test_data/repo_yum_01/repodata/repomd.xml000066400000000000000000000050751372027637700243350ustar00rootroot00000000000000 1347459931 4543ad62e4d86337cd1949346f9aec976b847b58 68457ceb8e20bda004d46e0a4dfa4a69ce71db48 1347459930 936 3385 aeca08fccd3c1ab831e1df1a62711a44ba1922c9 52d30ae3162ca863c63c345ffdb7f0e10c1414a5 1347459930 43310 735088 a8977cdaa0b14321d9acfab81ce8a85e869eee32 4b5b8874fb233a626b03b3260a1aa08dce90e81a 1347459930 807 1910 735cd6294df08bdf28e2ba113915ca05a151118e ba636386312e1b597fc4feb182d04c059b2a77d5 1347459931 2603 23552 10 4034dcea76c94d3f7a9616779539a4ea8cac288f 949c6b7b605b2bc66852630c841a5003603ca5b2 1347459931 22575 201728 10 fd96942c919628895187778633001cff61e872b8 c5262f62b6b3360722b9b2fb5d0a9335d0a51112 1347459931 1407 8192 10 librepo-1.12.1/tests/test_data/repo_yum_01/repodata/repomd.xml.asc000066400000000000000000000007521372027637700250770ustar00rootroot00000000000000-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQEcBAABAgAGBQJRB7n9AAoJEEavlYoi8sTpA/oIAJEnJR+JSOPbFRYpihOUDzEn nA78dNqtytzohhk3A+AT3/OtHTjv+TpXM563Yplo9HBfFP9AuSGJAZr8r78zeUgq jt7AKZkxvxf0T1UwWynSflfHhySvCTaBoeCeikKWt10sX2H6QvisJaNoazN/acVd /nDos6QLEyl8DeUyi9Uwjf1/8zUeaQ5sFo7vizGfOycbF6CQH724hNmyT5pgN4Pk PRxaLCJsIFQYPaAFKTVpXqVcxggxcLgao5SfMkiEr1tge9PK+VUrAFGx50VjkuLW O55PH2oy28q3o2qmP7DQb0gaEz5e14NMFPxwuv1OU88FitoknGr6OZwa6RIxQHo= =r/UV -----END PGP SIGNATURE----- librepo-1.12.1/tests/test_data/repo_yum_01/repodata/repomd.xml.key000066400000000000000000000033071372027637700251200ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.12 (GNU/Linux) mQENBFBXDKwBCADA5jpCwpb/JKOG8mcFyIanNojDwpHwKoyjGNpZNPNUDJguvkRa IO3NdoyXYd5QVTOsnyKBaRaiLLJWI/VJxTOT3fwOPprrzUlkHwoWl+sYuSdXHASu m4lkBiXHsa5oiXPdrY6hoh5vsF8ASwCHXOwpR9yyvGEaUUMBl2GpJAX/cGVcL4Dy Z0pyJMLO4qrIPoX+wd1ZSFSc8JcAC4UtA82HCGTmesgialpwKdoQyt+em94oIM1f D6v7zzcRX/zLKKEzpFnU458WBA+JACkde3ohX//0fDCeaLqMzs++FCgwm/HMCszw RnINr+K8ENfMYBoeM7a7tnhiae+rkxWmvWz/ABEBAAG0LFRvbWFzIE1sY29jaCAo dGVzdCBrZXkpIDx0bWxjb2NoQHJlZGhhdC5jb20+iQE4BBMBAgAiBQJQVwysAhsD BgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRBGr5WKIvLE6fdyB/9OzDczaqGy 1rzk7Wp2C9S5QatFUFNWt6FIFPITbixT4jrDo/LyUJVWLw4ng7ldg79vmrzhpP8h yFVvuvGvSEMn5sgnZ83SEd4vRJ2O8K5RuVs5Kcj7ayLlxPpqbYOYmrmTaLwYTwdv 0wDnNU9IkkMSK752RQes4J+4XGikd8CNm5lw8cRQ7bcQd8s2rnCoiyGt7PIdl13z 8hO9KA52iUP06AbbIusbQ1jzsViEny+xQH7SZ53Ga4eRr0mW2iA20Mkp4Ieb+dNo 47Q8aHUqI9O4HTF/3Fzt7KmNxXCpCOhxTWx0IkqPGoZ0W63Aut/CVh0LXsBF2TUD Ym9P/IjRgJLhuQENBFBXDKwBCACvhlMcgjLJ4PtMTtauF1OXVTfODQSHo+qwKt4S GyDlTGayQ76pOqYkkzIRqmNYl1ThmcfzpmJ3O+hFQQ7OdguYcfkbfgIMjEJEbKG3 wsR5pm9zjwStzYHedwkct1nyROgBz70o16FfdiWOguw58jQZOSO/I2S3JpLsLgI8 KqdIk/0WuoOfzt+KcvL52lX94O2hBpRI0v6lDgSm7KkPGQrVFnSIUR5r73ceageL 5LmGm1TlEjWHwA9iYIvBcjnE26/l8u58IYQ/sUmn0u4jBcBNc7iqdWvlSLZLlMmi qnzDNhUup9neKGxgr4hGAblxiSxXlmOoFv0jEW81b4VximSJABEBAAGJAR8EGAEC AAkFAlBXDKwCGwwACgkQRq+ViiLyxOnf6Qf+PIG//12qp3hXZsvB7JQuQ4nUNwp6 Ufm6W9pFm3DOqnI9H9ZNzGbkoS5WwRp0B1NLfNKipQVORnDs6qve298ReRrmLKnk BPZqxFpPqLQ6X83Or2bqKiJS1axonIgqkImFLfxxqKoukvhn328Z2FVlrvkKSMU8 eHi/iDF/TCHoPE9WtnVSzsNU9i+9j8j//GO+bMC5AGNOxcBKlChFpLYpE/pfITL/ icS7wB9MrMLNvjlN1EKszQFxJrFVBGTt8hUqRH3CCUFRwbpE1QJ1WAzJ0Vzk5nWR rVZQiiLe03B8hC7/qRiB4bya5nbWcwe9ltPFja4/tTe92ivScFfCLyALVQ== =G300 -----END PGP PUBLIC KEY BLOCK----- librepo-1.12.1/tests/test_data/repo_yum_01/repodata/repomd.xml_bad000066400000000000000000000033151372027637700251360ustar00rootroot00000000000000 1359079470 obsrepository://messiah.leuenberger.net/SuSE/11.1 d1e49565a4e4c3ca0ace622b9931e112a2a07567 1359079470 50472 880506 f8b2a1f13ef2cfe325d76bfe89c12a5290c53e96 29cef9e9007a55b7c56a823ffd364cafd758fb9a 1359079470 140479 1625075 bebd7a8b6b85e9c31275a43c92302c6410a20270 3fb1d331ec069e6eab9461d00910f4c87131c930 1359079470 51264 639419 0077943317eead251836ccbe561041f2dabe43e9 3f5f2513b1a6ade726f1c6369130b6bf54ff904b8061589e32adb11dbdbaa635 1359079470.42 11db26be9ac4eacdc93a63d8676f9923c4b5f9f388d166456cb88269793f719a librepo-1.12.1/tests/test_data/repo_yum_01/repodata/repomd.xml_bad.asc000066400000000000000000000004371372027637700257050ustar00rootroot00000000000000R ]|P r)r*S*b =}nd??5 6vUϣZ}_빀ѶL xBD1NMLzWyv!_0,"LցG" ib9(&PwZ4AףQmL`ڝ6] cLHq=R _] ^o%q.9>{۳R[9!7϶xյr!/m%˶-C|librepo-1.12.1/tests/test_data/repo_yum_01/repodata/repomd.xml_bad.key000066400000000000000000000072421372027637700257300ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.15 (GNU/Linux) mQGiBEV63tcRBACQY+H4pBYY9YBFbK4rJ79UXCDa0JKPmbCUYy++8SSdWh7SOYPs lQiWw/IAj+OkUUqxQmGxZVdMjwLzWKGePR+sEZ6vN1AQ2ZrPlPJDns66rWIfhDPo ADYpyXKyM/7QBv4GPcHZncu7K/iyqKj6mHDIKP/Mq5Mc6+mLKNKVtlvxVwCg6NFR r3ZHyrsrrgqjDxhBwIRiRM0D/3ctq9Fv0+bcLrt6krqF9Rp2LI8ZBmDtd9u6O5rg IAfp12L1OuJUgIEMOBuTrBFooNCAZaEcjTizlJ4OPmivEJLaxhDEGC3KCA0nEnVD w4+Mcd8kL4gxybqbU4r/aCbPvqNHxrRag+OX1mUdYdWSk5EyjjC5yGPJlHENSOAp 9dVcA/90J20HksR1D1KUO/Zb42MK7EmJoHENhnh4hk7kgjaV2rvORj5aDuU3uZZ3 Ag3ZAzjRTmMVhMt2e63pTTqh+60YQ0BBAm2WX14rO222yMYhWwNsmIJ/GwEMBj5z XjCwKFzh1SlPh/mu/SrtDb7JTi8C531ewdzJdqtxP4a/sJlKELRURG9taW5pcXVl IExldWVuYmVyZ2VyIChWTEMgb3BlblNVU0UgUmVwb3NpdG9yeSkgPGRvbWluaXF1 ZS12bGMuc3VzZUBsZXVlbmJlcmdlci5uZXQ+iGYEExECACYCGwMGCwkIBwMCBBUC CAMEFgIDAQIeAQIXgAUCTres4QUJDP81CgAKCRAz3o+3yNqT0ssFAKCSL6w5nJ46 TQmzkAaIDLEdAp+S8gCfYBnU+CUv+xmgM2OA+L7tuYyJ1rGJASIEEAECAAwFAkV+ jHYFAwASdQAACgkQlxC4m8pXrXyt9wf/ZqgS37eGwf25skyATB1vC/hv34ygGJdF y+54DTsh3MdIhxB2eJukom1lnwo1Hi/EngxSlfd4b6HUR9m7KH9KPxro/tByLJog aTHgGOiNhZTCAC0jYxXVbJtr8r4dZNlIkEAWNSBJ26xSevl80giuzyLlj84OHbEU bY/0TSUY6ZSazVFXVt5GzjPr0bsq9vVaXvSdYVMLlGlrIGt+yiwmbzYGsfIN+7zD QUvLMo5s49S1P8r4Q76M5ZKch6HsaR0Xi1+CA2LPpc7HAc/JNoQ0tFKK9l2FIAPh qGd2g/S3VLZt46gPzcYCd9h2mb0dx3IGIxQg1E2qRijAQsO6qzKrOrkEDQRFet71 EBAA8DHbU2Buqy1mVMn5oCd46sd0EZsgtrELHPxi2v7vryi8HQyBomKXdVwifurG dtkr+yeDmO0QDouI1YGJn0Yx0yGEPC5w/5nCiVLVGvJxMq8efmhQEJ0yU1DIrNls MUjhNpO+9rRFf5UL8DeLy5vGyjEOuyJPyolhQvOEEimmpbVSuZj12mnSGbKDq8sa ihAL2W/r3eDeeFOqFikTjxk6ANSc7z2kIz25JutunNXfxgaWhwGks95mnBhDWD7T 2RqUCmI7uk/9B6bLN4IUPMCnyK/ZjZOjWXeDaNdLtMMg1xOYRs9DB4LbM7BjVgwH x6NzFR2tuU5BGlZRS2br365LFtXhmO884R89g01hjFIrRDL3gBvVCJEl5r5U1FfJ e5NsQ1wcc3QtnOsJEHNgKJmrXVNLv5j6Pn45vKSRf9Fc5ErCD4yUqBV9NdTPfOSv M+N6cs+VfJFY/uFrTweY7eXj9AaTL6wGkXPWsUqzzhwXv4wksAfTELmp0CMi0qCq 1rKyanC1kVYegQuyENaO3g4hgjyrqGPvrCpW0LNIppRBN0cUYzBpsfIcl3mTQ6S/ +gU1tJdqgcJAqRVX2wf/QY3OUvw+/IZRMHuL8w+pDqKWpZ+DQAL1cuezJfKB7E0g H67ce3um2/wMeMLvH1HeTAthwPMW+uzXW4SVdv9onfTkQ7MAAwUP/0eAaluTdxV2 oZ/79dNXIvpojvNcIwbjco7avMLOfHUoE1VCg84ezUxMSijYySTgc5rISx8re3ia 3rhcrSYlVTCdlQh++kvOO26iqiDTTjmTHRjTxXuK/J8ZSSmfeZ92669WpO5wB58U jFdF7ensx/WbY1hms3YPKoLdIMgbgELBqcYHebY94Of7j4s8JsRxbzUBrLLUOVsx EH/1H7atTMKcKwwJSOxMxv76oQnx8jBwI2/QEYxsnwdTRp5TqPZKHsT/dgURcXjz /GJjOltvlnl35HZW8dnU213XtZAU12FnoHwjQiuhDNvN3Ghr99JEvoWeAE4H3UrQ Z0TSfi3XWwESmBCvPyF++0X/688AfaoIY5/G3x+P+lgn/5BMuGNZyqYM7rsh4g49 Wo+0o/+ewBkocnHwNQ5vlUzdUyoCbGvEOXiqKa/1bR9xtAzt55NdUXkO9UWmSyFS AK/KovOqkj7sqIXkD6zCaBF1FN3IsyLtyyBbO4gacMKAvrz5U2rnyEsm2R+mDuem IsJh2eHvLqPJ2s8IfzKQIkHZepZf7Zg1LP0xN1M8P7qeK+Fh24NdC5oSuNssLVw4 l6EBjFaIk7qlCyBtlMEtDgKO92Ual/EH1IFxhwy7ZuC+SUCjYZNGkqmymvDMtlCd eS1uS9wHwhWSsa8cxRC+mA0LkuzKwiF6iE8EGBECAA8CGwwFAk63rOkFCQz/NPQA CgkQM96Pt8jak9IJrQCgl25SU/jeAq8ywIHaKo0eG+bIQtUAoLd/GevsWDjiwnW7 oPwQZB/s7mvPmQENBFKAt7gBCACzwXu/TNU9hrAcuYfhbi6/sPOaqbXTi4VRe4Wm D1mttQ7jpnmN5xpenvx+aNcxLG0FvmZQjwWoPQvUgRJNpN49QUrs8QUBT0f8lFnj TdQYYHfoiTGHXFHriNZAPCxeHA16Yo+GWLwt9eCw4hwK7cRZ72tTJBA1AeP0ZKg+ 6e0pPDSWzSFmUc07xwqK2dxU6gZ0BYod1fABhaUApUtGcfJKJ1q9tuqISfSsiQpi ducwFZH2cEiYSQz/PbaBUmChBI4JbxTcdHlnLxe2rytFmg6Y/BlF+1q3/RmD8Cex dxiELt7nJHNvEPmxXX0R8qjkWIgkgxWFrKuIlgigv3VthsJVABEBAAG0Q2xpYnJl cG8gdW5pdHRlc3QgKGtleSBmb3IgbGlicmVwbydzIHVuaXR0ZXN0cykgPHRtbGNv Y2hAcmVkaGF0LmNvbT6JATgEEwECACIFAlKAt7gCGwMGCwkIBwMCBhUIAgkKCwQW AgMBAh4BAheAAAoJEKWVXfy6otISfmQH/120S+pklk9K/K6C2ntPdhaLgDT2Rffu x3Vi2fgpNrMkU+dN/wiaC+xg8m4+Kec5Esd1IPLIBreQfOtKw6+vg9fJNJp9F6Gt IM0kpe8gqbhlu9WoH58NYW2c2T3f5N6UTd8PO0FxuCgSFbEBDpr5xAB/zG8aMcer 7z+IgRxjLr9PjZW82Ac8mdM4EANYAiZXa8/BNW9UbDH4YAZATIJtyXcSoaSc+uqv e8dvMobbqh+sG3UkMrNgnBRrhUc2bcoIaVkj6FCjYyrNcCGfNE6gJh99/Q3JwkR7 bL5AdOm1sqBsU/F4ow7Q/a6Vargk14p3Qk11jgpKGlKX1vWxEUr8C/s= =EYza -----END PGP PUBLIC KEY BLOCK----- librepo-1.12.1/tests/test_data/repo_yum_02/000077500000000000000000000000001372027637700205205ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_02/mirrorlist000066400000000000000000000001561372027637700226530ustar00rootroot00000000000000# lalalalalala http://127.0.0.1:5000/yum/static/01/ # lalala # xxx://127.0.0.1:5000/yum/static/01/ # coments librepo-1.12.1/tests/test_data/repo_yum_02/repodata/000077500000000000000000000000001372027637700223175ustar00rootroot000000000000002431efa18b5de6bfddb87da2a526362108226752d46ef3a298cd4bf39ba16b1d-filelists.xml.gz000066400000000000000000001245121372027637700364100ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_02/repodata]#I)c@u53tueKTfJ"2RIK/UǹƘrIEx?o\K7??Tuc/ׯ_ۮ[NO_˾VPҗC}zO_~\OsS5?O~//g~?_m_/(|Xҏ/ _> lK?w}unóo}@?D"eD /M$Я"Hh"~D&z|,^¼^E@ܾi}=.zH6#t3Oi^(pt4w/TuOt}~l(p*[m_~_X~ɓ?X忿z/&G㑶 uAVcUǪ Z ߭?W_k "=]Q?? 7 [$?X^IC_2zw> "=+U1|6ϥLx#X@Ϧz ߭/¹S8'c.d|"/uX/<|\^ RSHx}ԩ` j.K{@TpHOOWmϵ6/L| 'X/} .o vQlW>_kS>qyDXv /e[x,;mM?"68fRXw'/(F`'(`>7>Ϯ(c/&~ Ljy >︑*GN:%| &ANW5N=m nwqO bk烢,Ǘmi ^v-v-&sXv*,OVvq__rXv(ضۂ'/}}A,/eX඀x:\Bm-~_5ح}}<~4 >~2O]G+׺/X Uݚgr.ěmθw { wUΣl~UͯlQ6oXVVj㿜ξX=ͺDz߭)~PlcOW/r=n3ӧ q>+[߭.>k b>o( ?(J(g˿|P[@Qv'X闶\^$ޫwEQo\Q竢W(Pe>(J⽱Jx\QrEI{(e>(1_/\Qo+J߻n v裏ξUYBSĿ 7K|6$.$g](g]A=;vj;+k,0VquO_ۯEAˎŭ]U T:ht2]<>3>=,GFat{ɺDzwN>|G˚c៫௅}X]AV3]Lg&];7%dx',ۇ~[WerC}}<@9нe tVPp+yxxgmIJС`Z$̂ |rO?n=[;d ޕ8 `x^,`#C%Rߨ[ ~ܧdx,t<8gWo74 ,a#}}<~ ><>73(_ΡA,Qt_e'C ':pKw{{$=b}A..68{c=6^wvl bo*ُUKdo}׾GoOJ߫_TJJ-V {1S~wmp11gٺ/e߰Nx@;znxZXm IJ}}<Z*~ ؜9tgbCWA?׀x*SgX ,g(|?GP?`SAAOCA}0x,P%^k:Pp'4~o><9}PYa L$Pp< >K3R{!~0\?\Q?%PñF;.ˆw&cɈ 1,8v?3gӟq"kc<6VX渖5%1uCh%=}CcmWX94ǂ>cA~c& e Oz(~P9P|PFgs^pF'2Ofti;>¿ Q hX K[󙖂Lx6oeBKdCKAvh_-4^ ܻ] ] kY5 ©M_lEA*xr~uE|P٭H劉kA$IՑ'9܊lGtNM=dSO}. bُ}}<N.O;~ >84T05O}}<'p*pP%|Ty{BñS :P9p ֩ u‚SA  ~pNx%w*;~=88a֩FNNg!9)?e"=Ω]'Uy28V\ LowE<NJd)k槷xIYSs{G±Ϙ!:dΖ!:3Dg|Sb?3=/۾m_RtKA/n]߭x_xoL}}A,;] FkhSs=ñnF[7T >}㱐 ތ8/nӫ??;|P㊟k%(+{q5yogޛUU17tD8[}}<D~^<9P/5?z?3)~¿w2{ _Cp_C+ 8įY!~!ya2iQl6Fial6͆iQl6͆iQl6͆iQl6͆iQl_ϋ7'+AA=$œ B7OyHYA?YA@YAAYAB/)!:ZPjB-Cu!:ZPjBCe!:ZPjBy*T}*Tρs`<As`:As`8Ays`6o8 @ǩN;y=8|q `Cf=$'ƒGБ!ttBG!tdBGБ!ttBGБ!ttBGБ!ttBGQ!tNգwS蝪GT=r;UܩzNգwS蝪GT=z;UީzNգwS蝪GT=z;U_84+f?3gV̞3+yfϬܙ;Rgfʜ3+qfά733b͖ul9[f֡el;[Ζul=[gϖul?[ϖumA[gЖ!dB|5^3z~5_||5̗_~|5`߀|6̗`߂| 633gg@Ϟ9={z 33gg@Ϝ==sz 33gg@Ϝ==sz3fϘ?c3fϘ?c3fϘ?c3fϘ?c3fϘ?c3fϘ/!ta]BЅ!tq]B!ta]B!ta]B!ta]B!tQ-(Y? gQ,̞ѳ(yϢY;Rga,ʜ(q΢Y7 F]>w-~Zܵks]>w-~Zܵks箅]>w-~Zܵksrzz]@QVVQ,=2,P(* = DTVVQ(jBVQ(qkBVQ(1lB N2m6Nm6N>m6N>m6N)qB)yF)qJ)yN)qR)yV)qZ)y^)qb,kAg%Йtf?gYsf=g%ϙsV;gΙsV9g%ΙsVޜ7gEX/z֋7^ o`pX/z֋7^ n`xX/z֋7^ n`ZFU te]?WϕsU\=WFUse\;WUse\9WFUse\7WUisLƴ1mnJ榴1mnLƴ)mnLƴ)mnLƴ)mnLO˝9YDY5@Ȳj"B&EYD8Z5@U DhZ5@U DZ5@U @Jh&6p:h٠ '6 p*hÙ&6hhY&6h h&6hgٟ&6-vT ʉS9r*fNЩ:crT S1z*gO>rT T1*gPhCcu!1:PBCcu!9:PBCcu!9:POB=Cw!;zPB=Cw !3zPB=Cw !3lCHWE =)uV\ Ί_A ΊYBbg,!bY1KB쬘%hظdž<6nq? zlcÖظdž]=6na_{lcظdž=6na|lc !uX cUx;|]!v;|d A> YBb,!GjY@5fԊS+|jfO評<5VԌZS3tjeNȩ85VԌiNtğɢ ~#"}O Gt,TTWߓ' #}O GL,=Y0{`8؞; 3nx3=l(; k; sjψ; 3i8; ;BChp !48BCh` !48BCh`0b^ t !*JuxtxtxtxtxtxtYj@ a4(|fOi< Q4F0teNi8 Q4i?rgNvVM(=,"H Ъ<cQVM$AiDVM$jDVM$jD ,^߰S7꓿ao$߰CF 7쏿Q{ o߰3F7싿Q[M;b8#gbL39v&<grL 31~&@#hrM !i|n|o|o|o|n|n|n|n~|nn|n^|nys`8G΁sp:s`<Fs`>s`@Ft`b_*E^o(*@u˪^L@Nj\:P)Eҁh/ w;؞`{7gK=a{6w[؞}a{7wk؞a{7w{؞a{7!w؞=b2 .ˢ*&K" ˡڋK ˠ ڋ2mg.?r\|㖟g->jIZ~cg,>b Xӟ˝9YDY5@Ȳj"B&EYD8Z5@U DhZ5@U DZ5@U DgѝFvFmtgFwFmtgѝFvFmdgѝFvFmdgѝFvFmdgѝFvFDIDHDGrDFbDERDDBDC2DB"DAD@D?Cixnxoxo~xonxn^xnNxn>xn.xnxnxn}K/gt}K/gt}K/gt}K/gt}K/gt}K/e >o 7|>xn C7x憏>pm qw\'8q;.Nqq򎋓w\'8yʼn;.Nqq⎋w\'8yʼn;.&?Ll1bb&] &ALl 1br/f!&CLl1%brOĦW[r렯^}J諗B_Wr9롯\}+D_&EWrY뢯\}論Fo4Vb[>m$Ճؖa[=mCV`[>mV_[>~mV^[>zmV]ߝzv"* Ce<(QC2@J@2Ce<(AxP*A ?Tƃ<^;0PjBeqD WM1зj"w&j"k&Pj"_&Hwqac?+~. rݏe?R=8 ˂+4. ,G ˂+. N|hbVZ1P+hb(VŠW9*\`fbhUT9* /vȁUBOJ B_Agd Ad Ad A'd Agd Ad AڎBx*AIi !n?^%B܁%!Aw!g A ĝYB^,!%ZJ JJJJ J J J J J J Z2j%P?f S3zj%O੕;5cV ZS3rj%N75flhf8q6 3g0u6sg8y6g8}6g863hiCg!3zPBCg!;zPBCg!;zP+kAe̟V2}ZOi=eʝ*uZNi9eʛq2msW]-Z?w|jsW箖]Z>w~jsW箖]Z>wr_RUBOJWV!*Eb,!J%KB쿒%!_d A0Bj*AI !\%Bl%!af A0 vYBb;,!bN^ t !Juxwxwx wxwxwxwlC[p[omն[j[nmuvٖ[d[omז[^[osmv֖[X[o[mUԖ[R[6y_y"\ S'W.}ޏ#C:: C::#C:2#C:2#C:2#CHZR[oIm%[R[nIm%[R[oIm%Ԗ[R[oIm%Ԗ[R[oIm%Ԗ[R[oIm%bЕ!tu]BW!te]BWЕ!tu]BWЕ!tu]BWЕ!tu]BWU!4li1C!eE бf=$'ƒw~5ks]f>w~5ks箙]f>w~5ks"kAha-̟E0}Y=ga,ʝ(u΢Y9ga,ʛq0m^.wdagՄ#"Ϫ" c0ehDVM$iDVM$ajDVM$!K̤7^Lzq3ͤ6^Lza3ͤ7^Lzq3ͤ7^Lzq3ͤ7^Lzq3ͤ7^LZfTS}n*M<4gTSy^Ki>,gTRyNI)IBɳBBBC3CSCsCɓCɳCCCD3DSDsDDɳDDDE3ESEgY:+ 393} 39+y r9+u 29+q ̸9+m ȍmM- ǭǍmMzJhC~BhhhhhhhC7:lUЃ!`ƒux*E!`YB!`YB!`YB!`YB!`YB!`YBbx@zR*<AV.ze A ^YB⎯,!qW+KB/vigUГ\Ϫ !N]!d As>YBO8%!d As?Y"߱fJT3jO>5fJS+wjNԩ:2fJS+ojMʹy_q3';&YVM$QhD(G&j"M&j"S&`j" Y"CVքS{ YY dʚc@d=D2deM$!+k" YY dʚH CVDzz YY dO!2deM$!+k"&Ȑ5@2deM$!+k" YY D=G;U0=2wX0Bw1}ȾSł̾SłоSłԾSłؾSłܾSłzZ`jw !3zWZgX!;zS`Jg!dB#Cht!4:FB#Cht !42FB#Cht !4*lk/lˆk/lk/lk/lk/l3: u|'NP;wB: ux'NP;wB: ux'NbC"ʊ@yR@(+<@(+PVxPVxPVxPVxPVxPVx,l03{n0 6l0V{l0vl0#̖{l ֕ZWlk]uZWnk]u֕ZWlk]uŶ֕ZWlk]uŶ֕ZWlk]uŶ֕ZWlk]% % % % % % % % % % % % % % % % % % % % % % % ;BChp !48BCh` !48BChPunו]Wv]un]Wv]unו]Wv]unו]Wv]unו]Wv]unו]WW't_̼DU"CV\y*%h,\yP|<^(K?W0%+AЕ e@&kA7%Ѝtc?7Msc=7%ύsS;7΍sS97%΍sS77M kuy(6Y!=@,@,-y(6Y!6Y!6Y!6Y!6Y!6Y!6;3/fpUȃi B]fguY"qa~v\g%f huY"j!Ԩ'ը ݮW.PnY"j:KB ]g@a,5v%ݮD tbAK|VE < ":p*%:pO\%d@%>Y":pO\%!pr# r wb b wr qvqqvqqvq?'.wdagՄ#"Ϫ" c0ehDVM$iDVM$ajDVM$!w\gXwTwPgLwHgDw@wb pU`X0GU,*r[VM(=,"H ڪ<YVM$alDVM$kDVM$akD^&ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&Ȑ?"CVքS{ YY dʚc@d=D2deM$!+k" YY dʚH CVD_<Ȑ5@"CVD&Y!+k" YY dʚH CVD&Ȑ5@&ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&Ŏ+5_ t|UxB]'6g AMdzĦYB0b,!x&Y@hbL3)|&f虔<gRLI31t&eș8gRLIiZ͢Y6 fQ,LEi0m͢Y6fa,Ji(m͢Y6fa,Lߋ ÷˝9YDY5@Ȳj"B&EYD8Z5@U DhZ5@U DZ5@U dz<Ȑ5@"CVD&Y!+k" YY dʚH CVD&Ȑ5@opyYMYDMYDMYDMYMYMYMYMYMYMYeWU`*r0|8 _"WU`*p0|9  _EWU`*p0|9  ]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W]W<Ȑ5@"CVD&Y!+k" YY dʚH CVD&Ȑ5@,"vyY\EQ O?j"Y^EQyu,"_E,"_E,"_E,"_E,"_E,"_E?&ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&Ȑϸ<Ȑ5@"CVD&Y!+k" YY dʚH CVD&Ȑ5@?<Ȑ5@"CVD&Y!+k" YY dʚH CVD&Ȑ5@? &ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&ȐV=]@[v =?j" Yo( Yos޺*2duwUdzȐ=W!뭻{"Dk\A^G:d uY#zA^G:d uYzA^G:d ˪FFFFFFFFFFFF&ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&Ȑ&ȞCdʚH CVD"!2deM$!+k" YY dʚH CVD&ȐrY"w\ֻH.]$.ze ErY"w\ֻH.] .zerY"w\ֻH.] .zǹ,?yY>'xyJ>'Ÿ^Μ,"쬚P zdYDY5@DUy Ƣ,"H Ҫ"4H Ԫ"LH ժd%Y˰JJĘJ̨JLhJ̥PJJ8Jʢ JJJʟIJIʜILOHOHOHOHOHOHOHOHOHOHOHO   º$º,ú4ú<ĺDĺLźTź\ݳU=@, OpO<@,,@,,@,,D?AΜ,"쬚P zdYDY5@DUy Ƣ,"H Ҫ"4H Ԫ"LH ժ"dbȰUjdL5:FpM#it, ё42F0E#ht !6/viѪ'%Gƒh"qQ(KB\|%!.? e AKD h1vg1{vg)wvRg)svg)ovҦLNi1m:M6ҦctLNi1m:MǴ6ӦStLNi1m:MǴ6O_eΜ,"쬚P zdYDY5@DUy Ƣ,"H Ҫ"4H Ԫ"LH ժ"dq3zGUSCgD3zGS`K=Cw$ q3zGQ C=C<B*AIiǪ !X%B%!n7}d A> čYB֏,e-#h3̟g+|̞=g3xʝ=cg3tʜ=#g3pʛ=f7mgc-;iƎ5vڮn7kWcǭ;qN4vܦ.7iGc-;qƎ3&['/?pÉ'/?p'.?p'.?p'.?pY2f%?gf 33zf%;3cgV Y33rf%73ff<>,wdagՄ#"Ϫ" c0ehDVM$iDVM$ajDVM$!;M(=W:>;M$!NyuW<>ȐG~^ȏ"C+Qdz#? Yx|G" . MYo"zA֛D&7d MYo"z@֛&7d MYo"z@֛8o5|ԄSC>j" Y_{GQvUdCV!kwYE=dvUdCV!kŎa58R#jp@ 48XJ#i` a48BR4{EKW-^4sK3W.^4{KW/\4{LW0\4{ ELW1\4{̅L3 PG($J@) ^N'@Ej PUe/,TxiBB Chq-!8ZB Chq- !0ZB Chq- !(섒tU]@WUse\>WfϕsU\@9 r !gUBOJYƒJu%!>ne A\YB#WЕ%!>ve@h#;6Nh; 6Nh;6Nh;6Nh;6Nh;6d-#RU S)~*O=RT JS1v*NЩ9#RT JyS1n*M ̛Z;.VE A j|@P5< A j|@PFb9?a8x;Ca8p7rgNvVM(=,"H Ъ<cQVM$AiDVM$jDVM$jD 3UVȳzHSYAQYoƒƒƒƒƒƒh-#HG 5XVlxb`ņ+6X; hUГR+UABlJu (KBl%![e A V@YB:bЉ!trB'!tbB'Љ!trB'Љ!trB'Љ!trB'I!4oh1C!eE ٓ=IɓÖy[UBJ<ƒux˳Juxx˓%d Wϕse\8~q 0!YbfyVE =) B,*E,O8˓%!d Amzx{e-f3mLfϴ+mL=f3mJ=f3mJ=f3mJ=f9ZAis`6́isP6Ais`6Ais`6Ais`6AiZM6=ӦWL^i3mzM6ҦgJi+mzM6ҦgJ6.&...-------@cp 47@c` 46@cp 47@cp 47@cp 47@cp 4h-#hT 3*~FϨ=#gT Q32vF9##gT Qy32nFxmxep]pUxMxEx=x5x-x%xLO?q3';&YVM$QhD(G&j"M&j"S&`j"Yr>߰O~>߲O~>=ߴO|>]ė߶O|>}߸O|>ėBB Chq-!8ZB Chq- !0ZB Chq- !(5-f=$'ƒbay,ff10هY1f)RhRkf/Zhrf/bh撡kf.jh没f.rhҡkf.zh9bPb%Pr%Pr%Pr%Pb%Pb%Pb%Pb%Pb%Pb%PRUE5<%<߿߾߽ջ߼Ż߻ߺ>?,ߗ9~$&X[īkK~-y\ƶ s?֖&X[cmI`叵% ?֖&X[XP+K?[O~s+TJ?SO|$'4ɇI?KQO|s$#X%6Jln(QbG[%8Jlr(QrFGɝ[%:Jlv(QrĆGDB'!trB'Љ!trB'!tbB'!tbB'!tbB'Y2J39+~L93zJ393vJ3C93rJ3笼93nJe\6ͅisQ\6ͅisa\6Eisa\6Eisa\6Eisa\6WY˴*mL+檴2mJ+ʴ*mLʴ*mLʴ*mLʴ"mz9^'§a4F0xNi: CQ4F0pMøi6s}=Sk#}'ss|=s{]=s{=ڣscz=e@<1=u tc{@:1нy tc{@:1нݵgs^]{vٵWcמ}]{uյgS^=]{tѵgC^\{s͵g3^\{rɵg#^}\ 7h*AA Dv_%|$.I^铸'yOb>}$/I\'qOU?~$/I\`z}CH秮@zR:BuUxOQ]%B!Y gUx]!6[d A~ v?YBbß,!O|!C" BU⯋ CYB,!qP|(KB\>%!.Ё; C:0C:8 C:8 C:8 C:8 ![UBOJkW!q *E0FB#Cht !4:)d r0v 7 dqa|@6 dq@Gqtx GȑArt(Grd0GAsd8Gsd@G0;yUГUAB\üJu9KB\Ü%!a0g Ak 5Y"v!"DC" " D D D D D D  4bBChf!4;fB3Chv !43fB3Chv 3jN$מL=\sRr̵'kN4מl9\{!eeeeH*s*{2){2){(s(s}'su'sm&se&s]%sUrΪuV] YuUx;]vVB;f UΪYBbg,!jZYj@-e*|ZfOi<-UV2tZeNi8-U޴Vi#}Xކ]Xބ=X܂X܀W~W|ŽWzŝWx}Wv]Wt=WrWݖ;o g2s-Sg:w g:{ g: h: ![BCht !42FBChd!4:FBChd!$Ν;w\ys;w^qsΝ;w\ysΝ;w\ysΝ;w\ysΝ;w\,nn~l}v͕3u?JrJJ'eIQIvr5E8 n4OJr 4\rgѯk{_x^pꅇ^pG^p^pޅ]pڅ]pօG]pŋl m֋n֋o׋p q# r3 sC tS uc vs wͲ4+f?3gV̞3+yfϬܙ;Rgfʜ3+qfά7҆2m&0m&(m&(m&0m&0m&0m&0m&0m&0m&0mF^׃~]u=z_׃~]u=z_׃~]u=z &.6>3;t&{St/3ٛ‹x&{"L*!L*!L*!L*!L*!L* AL a8{=LsDZq0s9Lq㴑_ףG]u=z_ףG~]u=z_ףG~]u=9999999999999999999999YS=;S=9ՃS97cS95CS=3#S=1S=/R=-R=+R=)R='cRevw;ou|6w9=oqxw7;s4]Uiz 3p4=JMS4=PMS04=VMSHZծS;3jW@L]Ѵ3vҮDGhgJ+vϮ;2ggJ, ^;2xe䕹+{W>^;2ze약+sW~ _;2|e+sW          Wә靈_Ng|=~:uWԙ勉_Rg~M:EuWՙ荒_Vg~]qqqqqqqqqqqqqqqqqqqqqqq#%')+-/13579;=?ACEGIKMO萵C t0ϡ9>`Cs0x9:CPCs0p96\̩"NtOVEğ)Dj`,"Q"(5MO!BS"@5MO!T"X5MO!B-v\UpTU`LT`DTp<Sp4Sp,Rp$RpQpQp Н; C3;C; C; C; C; kA Px H Op O[TTTTTT @pb5EGP^AM⿋AUB*!ppP~88J@T% F-vijtSt/MEB$B]%!NFW AS ĩUB*!ѿ!2VZBChe!2VZB+Chu !2VZBBQ2@ Gϣyd<2z< G#cQyT<2r8 G#i;s4]Uiz 3p4=JMS4=PMS04=VMSH$kVOJ'3ItzR4=1JOJ'ѓAzb=)>OJ'Γ2牁Ĵ.o")Q諄]UB.*!p}JAE_%3 :`db]W ]AVMf+f+f+f +f@+f`+f?oo;VUF&+e mMUD]VUF&*eLkRF&*ezu׍O_7?~uO`7> vCOa7>vOb7>,vOc7>%YJJJ̟ILI̞IJIʝIJIʜIJIʛĸIJM#N<>|O:>lO:>\cO:>L#O<оH|v勷n/._]x|v o/._]p|v o/.ZFС:@PLCs0{Fϡ9} 3} 3vE~+CWw\_>rE~+W[_>nE~ۊe+VmQF#Fn6m4jhmQF#Fm6m4rhԶmQF#Fm6m4ni)m6ƴٔ6fSlLi)m6ͦ٘6fclJi)m6ͦ٘6-vӦ(BJ6!&EUBUBUBUBUBU"BChg!3vBChg!;vBChg!;v8;9EKs)AM⿋ *!sJB; *!sJB+lV Zy&h=[Uw@+oVYygϪ[w>n|VYy׳g=Ϫ[pNq:逎S;qtO}?qu y[9MQi /B9M⿋ 3r 3r 3r 3r 3r 3r 4bBChf!4;fB3Chv !43fB3Chv !5-v):ЗRosSxۛAUBbs\%!9W AUBbsZBChu !2VZBChe!:VZBChe!iC(3C(;2C(;2C(;C(3C(3C(3C(3C(3C(3C(+0-f jj"55 lj] AU"T% AU"T% AU"T%+D-f]5J):ȗjXSxЕĚ@QbU"rX%V%+GU@QbU"rXlCH})Mvn /Bd&Ed*!qs8ٹJB\%!Nv';WpE6Bm|{ 7@~x{·7>mzx{@r3{ N 6m@r{ 7?q{Ο۟~˝9UDiBɪ4=BMEUD8jB)DhjB)DjB)$ȊqN(P;wB" E~'N(P;wB E~'N(P;wB4&o`M5yk X7&o`M5qk X7&o`M5qk I|B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}B}Brj+FjjʩѫFrj+Frj+Frj+Frj.k6fgL]i3mvδٙ6fgJi+mvͮٙ6fgJi3mϸܙSE*D?MS(4=XTE)DPjB)DjB)DjB?.@ֻȐU5]쫽 YUSȐU5=.2dUMO!CV2dUMO!CV2dUMO!CVdᶟ,Pl}>:$|}V}V}'쳪쳪@2En)rD8Oq"7NiȍDn%r$4G1"7MqM3Dn!r4?!7MIϴXyS%(J9ME:MyNx‹z‹|‹~‹‹‹;9&sL<1qc0a9&sL81yc0a9&sL81yc0hC(1C(9C(9C(9C(1C(1C(1C(1C(1C(1C() Z?q~`Ĺc'O8R?q~@yI' O4J?q~ 9Ic' Oic&mM)6qOlؤb&nM 6i/lVؤa&nM+g~g|gzgx}gv]gt=grgpfnflƽfjƝf)Bb7EGPs^=M⿋=UB*!p}~J@s_% DЉ|J'NK:uD؉|j'N˝ȷ;ѯw"D|'OKȷ<`(Ll(Lj(Ll(Ll(Lj(Ll(Lj(Ll(Ll(Lj(Ll(Lj(Ll(Lj(Ll(Lj(Ll(Lj(Ll(Lj(Ll(Ll(ܙSE*D?MS(4=XTE)DPjB)DjB)DjB,߂3333333333e%PaOQOQfOa%OaNaNaeNa%NaMacm4cm,Əbm$ئbmkئamKئam =O' zr=1!te]BWЕ!tu]BW!te]BW!te]BW!te]BW!tex!EK"!$B<)JB<+JB<-JB} 3g8p`>Á} 3g8p` >} 3g8p`>} 3g8pp>} 3g8pQ 42FϨ>gdQ32xF:#CgT挌Q32pFȸ63z>3r?#3z9?3zY?3zy?3zș?-vgt->[|Fn3gt->#[|Fl3gt->#[|!te]BWЕ!tu]BW!te]BW!te]BW!te]BW!teI'pxxgxԏ'pЏp̏pȏgpď'ppp$-{ '7N*V8pbcɭ{ '7N.^8pr-{ '7N2f8p֊[+&nb֊[+&ob֊[+&nb֊[+&nb֊[+&nb֊[+&n$Wc\s5&՘/ f EyQ0v^:/ e #EyQ޼0n^6pnljh}f]d=b`^\Z1Μ*"4MW!dUDiBD Ƣ*"5MO!R"45MO!T"L5MO!U"d5zvk٭ѳ[gFn=5zvk٭3[gFl=5zvk٭3[gFlToH~ToGnTnF^TnENToD>ToC.ToBToATo@So?So>Ez|#GOA=>r}1s#GOE=>r}!~']O˞ȷ=ѯ{D~'OK>}D~'O˟eݔ@7ЍsSܘ>7ύscܔ<7Mscܔ:7Mscܔ87Myscܘ6ܙSE*D?MS(4=XTE)DPjB)DjB)DjB$kVIISPMJIGIYDI)AI>Iɓ;I8i?.p||>T}>T}ňJb|.#H}>T}\bU_1هaCWx5=oUetkRD] lM-kUeTkRƴ&*eDkRƳ&*e4kRƲ&*${yO>}hm_)_C˴$BY&'SC,$BY'2#?IPyIO/e~~l'}+tt"CtCOK@'?_PtC W@'?_޽du/{ ^w⷗dl/Z; N~bdc/;I >}_pwMU>\嚦iz>kMS4=eMS 4=_MS4=Yq{mYǎ7c0߂u:6^nNc:6TFc&2uf0ڹӮj^]v~;7u8rs]mw;7u8z'DZqaQA1!̞|Ƴg6=왍gl>{f3Ϟ|Ƴg6=gl<{f3Ϟ|Ƴg6=gl<{f] 3vϮٙ>gg쌞]ɳ3xvٕ:;CgW쌜]3pvθٕ6Y2m&3m2&+m2&+m2&3m&3m&3m&3m&3m&3m&3m2FRgN5Ξj=8sqT̩SgN5Ξj98{qTƙSgN5Ξj98{qT,o3_+gW~f9ro3_/g_|9s;̗o3_3gg|ќ9Us?!zu:^BWЫC!zu2^B W+C!zu2^B eݕ@wНsWܙ>wϝsgܕES;ES8Eis\h1]""%œ"Bg` J3(wΠ:2g` J3(oYhfa,Nő0sY;sga,Nѳ8{Y?ga-N8\5-vUsSt/WMEB|$B|\%!j_5W A WUB*!Ab&n4EGR^ ĹM⿋ UB*!qF8JBQ%!(kAhd̟Q32}F=gdʝ3*uFΨ9gdʛq3*misU\6WUise\6W͕isU\6W͕isU\6W͕isU\6W͕isU㺿Ӻ::󓺿󃺿scSC3$GR&?MM~<5jGT&?UM|\5jGV&?]M||5k`!h"4])A6A36 )UB✍*!qF8kJBQ%!Z9iuנ՝sVwY5eu]3VwX9au׀՝UwW9]up՝UwV9Yu`՝sUwU9UuPeΛ3;qfG̙:3SgvΝ3;yfF>gfΟ4;fF B Ga8}>Gq0u:Gǁüq8m}_gJ-}_g~N=}_gRM}G_gV]}_gZm}_gy^}}_gb}G_gf}_g~j}_g~n}_g~r}GEvB -Z|'Nh;wB -Zx'Nh;wB -Zx'Nhe%P`OPOPfO`%O`N`N`eN`%N`M`MLLȴJȴJȴLLLLLLLȴWstWsdWstWstWsdWstWsdWstWstWsdWstWsdWstWsdWstWsdWstWsdWstWsdWstWsdW*Wͫ_5|ռUW+_5~ռUWͫ_5|ռUWͫ_5|ռUWͫ_5|ռUWͫ_5|,aƁaqaaaQaAa1a!aaa``elJi1m6ƴٔ6fclJi)m6ͦ٘6fclJi)m6ƴX6?`m|kX6>`m~kX6>`m~%qaQA1!~᧞}ѧ|YPdCyE9PdC}E9PdCyE9PdCyE9PdCyE9PdCyEChe!2VZBChe!:VZBChe!:V22222²2ò2IJ2Ų2Ʋ2q;gh=D)wѸ{Ɲ4q(;gi=LiwӸs5qH;gj=TΩwոsF* )) )) )) )) )9d-PLis(m͡96PLCis0m96PLCis0m$ p2' x2' p2' p2' p2' h2|~G *5EGRUj /j  ^ ^ ^ ^ ^ ^D ڳgng͞=s/{V̝ٳgncŞ=k{سgn_ڽy=kzh恁yp<0s:L΃sy`<8{>Nσz`=8B0+D2O<+DN<+D2O:+D2O:+D2O:+D˷'\̩"NtOVEğ)Dj`,"Q"(5MO!BS"@5MO!T"X5MO!AHWUUto.LUDU OSUxSUxRUxRUxQUxQUxPx w@*:ȗz‹w@*@*@*@*@*@*@b3f9s90kaì1c3f9s50sa֘1Yc3f9s50sa֘Ü_q3;MU>YQiz>GMS4=MMS4=SMS`4=YeXJ1u2Nt:M'sd(Jqt*Nѩ:AdJs*sNɴd`8w>bˈEbDU1>}>T}WbU_1B؇CW+fi%gW,5/jAE ?(P5H/jA_Ԡ(oUl"Ū`^U @)@)@)@)@)@)@hA 40Ϡ>g` A30x:CgP A30p6 w|^:s|ûǷ8 o|{û&ZMTDMdDMdDMdDMTDMTDMTDMTDMTDMTDMd@b&7EGP^IM⿋IUB$*!p~8J@_% NPq%EJ *)UR8hPIᜒ1%ESJ ()QR4p@I|$EI &)MR4<@4b獆4EGPO^%M⿋)%UBᜒ*!pRI~8J@VR% + z[CMQj /BCM⿋ Ç Ç Ç Ç Ç Ç oB{EKimSxw6.7V Ap čUBV*!q3nJB-vm !96BChsm !16BChsm !16Цl1Ch>("_j޶"ٚ@h* ܻV%{֪D 4pZQiU"\OaOQOaOaOQOaOQOaOaOQOaOQOaOQOaOQOaOQOaOQOaOQϫ@ +@ @ @ @ +@ +@ +@ +@ +@ +@ +@^e-#2̟*|̞+2xʝ+cԹ2tʜ+#Ĺ2pʛ+抴9XyS%(J9ME:MyNx‹z‹|‹~‹‹‹ZBChu !2VZBChe!:VZBCheU!-~pK-퇶~hK-~pK-~pK-~pK-~ȱO]|3.>oqO[|-?g1OY|33oVj"Vj /"YIw xVA]U"YJ?+wUh*U%ZOni?[Omi?Ԗ[Oni?S[Oni?S[Oni?S[Oni?;OS>N;OS>N;OS>N;OS>N;OS>N;OS>P!¦("_Q MEBl(l]G6V@ U"zdCaPX%G6V@ U"FNn:FNo:FNn:FNn:FNn:FNn:|TWɮ]ͧOv5j>|TWɮS]'Ou5j>|TWɮS]'Ou5ow'ʁ W ?j q?j v~K !BY/_(K e ~,/%B., ]A_z`E.p h=$BXC.L L=DB3+.@ֻȐU5]쫽 YUSȐU5=.2dUMO!CV2dUMO!CV2dUMO!CV2dioUOJgaIƿFL40~v'4X~v4l~v4~v4~vTvs][غփ.uA_]VzXՅBuK]HQA3%("]" /" %"""""",kAY @?>ٓ=Yɓ9Y./ϿG-I7 26e351e1a38eb1524574e86ab130ea4db780aa1a4a8bb741d37595ed203f931c-prestodelta.xml.gz000066400000000000000000000005201372027637700364730ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_02/repodatalPn E m]]W+~8}}}!nTV{aFq(SJp0mcE"INM3e-PQM`pD2@=B訣\Γќ*ꙛӠ1c|]7 J`7R4jd$`l}jS?j)^Ps;|o?d %,egK.Xar̍HU6|Șga{˛ds 3 TroDZK3`mej欁)L:*W%l~_>32d3307b672abf7356061912fa3dc9b54071c03a75c671111c1c8daf5ed1eb7e-deltainfo.xml.gz000066400000000000000000000004541372027637700361160ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_02/repodataPmn ){uӤMA9ު6zҦ;onvM_ Ѻ}rh#Wb }9$`eR0Z^UAC%_0FiaXWeFe⧱3vA/"%Djgq2/(Rq}][1 5ז˰R*/8yKR0mSK!lS)R`Oe}e~ 5a8e6bbb940b151103b3970a26e32b8965da9e90a798b1b80ee4325308149d8d-primary.xml.gz000066400000000000000000000016741372027637700354530ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_02/repodataW]6}_aJ !CȈL_VJS1lji_k C*O89ؐém+h#q$W],P-XV2¥#),#P\~]{t0~`'vCw䒵PTs6ڜni^"ҜC77FSUhh\Ӏ^?ҚRx.i [dAooM |ʘmxݖ/YɶQpvDgQU,NjK!#>[ 3RhV3iYTJF>:ȥ ͅ(p-:afy.a@TE[8ŬCi,k(P C<JZXfՁfM53w6Bp mq=߹K`&u]헲[&*>΃tJt |8> OukOOԇhIp|m}8>醗2?fY8S 5b37f89f9f4474801ec5f23dc30d3d6cf9cf663cb75a6656aaa864a041836ffe-filelists.sqlite.bz2000066400000000000000000000547761372027637700371710ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_02/repodataBZh51AY&SY:H _ϦOlO׶w=_O=Wp]wѸ=6}  YJUAB9(2mI Fۛ̕mE+8TvuEr H-5gI(( `h44iF& d ѦL4L dLA&{?4$P!FOU*ekYO~=>?7~z(D:DFMٯ>ű 4`&P@|T@sZ-w fm}OD5ΕyhЪC&d>r4:7Ċ4V>\ wM-0˚w/v <^C!2 U*ЧCF]ԡ.CN,uҢ,GLxJz jۡ誕]`OkGVcnPs-r^N!)it? Q "I"$R"xqV %CVHԅ`FHW QD]k9۾s7HEi(#B6~G~y}9]K 7X-%r.CPxyl#H"qd+L -%&}ʻ6N  XyOHt?~Nyh3"-X >W&A"$15P8ɮ6 PnY[Z 7u/nJM1/,7sH _r>߲Ӹj瘊lϸ=6/#7wYL:K'Xby܂4Ɂ @Q&t&} PSsAd ۙ3fmAXsq_'o?) ;PYL)db&(֠?qs˯/$zd+ܗg9ulZ\y YFw_Ď# b#xF.|ɦX10g'ι'Iޱk9ϊj4PiɩG@&h-.-4~=@2z-oqvaojM9.I;[Phdʙ\n1-7#EklG']rMҒ k$ya-_6 B7c!&d-9 oB-CÀ֡0ԩ=LQٳ$r j~+ toY.$&tgE(_pL27^'r>ҋV[y)@OI*i&XsVݵ}Jְ4/:s].t)xRB6 $ٵ1B=,)We`s V QCRZ10{ݔw1"/`c? /XB9{2If4~W֜cЃ=]a})} >4ӎOcA6I {TlB%$M ibc9GXA>(Z 7lt~;E;(c| 96Ά#V1zH5vC`d!}͜d"G1+Ή,R,)[P11{f3X'چL gKQ bqxHkJԴ$/X1@F2r{j^"Ȥ4ccLnB‘K`N֬ :jn'm.aqBa^?t+Lc#xFK q4x#_/G];4^Hr"g`ClX{?,IX9@,5CV"^L,pm/N*m,@%)!i2Kr  c{(,Y {ppnQ*/EXc1ܲ=WT 0"2ErzъЄC{|h9~*oW4(^FdTZ /Rn%^k?Zw"}]`.yA14_Ā1+!;b5%MPң]b$٪K$ϻG0Bv̛tBtst9E줆ӦIVUR(JRpj5-8r34$[,ܛgxuuihNֻn$GA3߮'N3Aq.eIFFCowi5RHĹݘ!O'^9G ĚW\pwuvݭTmkkFNm4mÒsepp$'o[/OϠy#uj "k{Y0}6,s$'loy{LncNY7dH$XΑ=h>,L|zxh ]f( ս6jZrjt ŏtE.fxգ>9;/<'|yR<;^^Z@@MN% ʃhaB9}^DPؐ/33'0!EY>~_s , $A(̶mYYyV_ۜ|UgQ( "|/͗'|t{Jqt7.*H݁"Pi!$h9FFP+'w~6KQjVm]a|e|$?.C T=.#!/:]؄:y$,r 8|B10t`鷗85t/>"N LX@ pR-Zmfc#ɑF e2i#" >hn$M%2HCZ!' h.Ώs|6jEί I‚s:>/[[l9SaLK :&>d>@'*t_AN;$HD 5W-~&ck|19lmA0 @:w-|P?hmc^yhv8E=FAk)rIo4y| F/O^fU4]Ig_/ 3PX=8gJ,m,='WUm䎑6vH @Kr9>~` 7%I?)3>WfyTBqY=,('6cɝ}>H@-o})]=6xEaejE=G\$˒Iu|ڜ;ETsGVBq~#(hk4zw cNCc#)Q W"RA"l9P@ k/=wZSdf6xBuT%*^qqHYV- 6=T|^bxyTLtgU.Wrc_'K^ssjS? k}gSTogs {usyTTӡdž5R' L]/9[^ aϣ3_TL{k<* W$ 7._<KٗL}I7dI@m(gjm݌Ukj=g o H(/_lj!e NU cv$|ԩP?|1UTx/h;:^sxKE& %|CGuɝ;JsNW0*MziDJ."nQ@kR W~ѱdĉ4{ǖ$AFHNݶݯc-]T뽸$4]Md)(ob,ʦO /A2!EP "KnO'ۓomomoɷxŽ}*̹r{3|in2?γ$븻6d:;/G'IuUmU[>/jIdKE>w;+ƸtQ5R&UazV $ʅzN*>|hiVu?'Mn8Mg.Ng3C=G{aSKY508mFn"+SS[\Y+\_$Tmz=^zG Kn{7w=>Ww:nW^;|TyU_8}7qwɫ=7J*++/UUUUUUUUU]ZUUUUUUUmUUUUUwvUUUUUUUUU{*U{ܥUꪪ|z%U*lǯ{}{W*wt""""""""";xq QEQEQEQEQEQEQKQEQEQEQEQEQEQE.DDDDDDD`DDDDDDDDDDDDDDDDx""""""""""""".R(((((((wb"""""""""""""""""""""""""""""""""""""""#"(((((((؈"""""""""""""""""#ʈEQEQEQEQE.DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDs%QESޥ؈DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDnnpڀMMl-Y6IG5_3،6!e}tk6]ξ7OtKS׳V41Nt|ǚV]M^V$SUYVakEC6$KS3i ;7m+SC/ͶJT0+qlT],} 7uh2Ihw"܍Z?5_,i0K\ܢQz%HEL =.F.rջƽit<-C̜T|Du*N:>󩂉?ō7^8K*XLvPyk3!tB*~M+=mqFVVgދC|nKEm=b*Ǔ{fͻ0$L "jG?y:9aQἌR( 0"b B2,A$"H( ]=i׾4MQ9Ft3m~L ըj[n%7w)jAB "A#6Kf}Ғ!ȓ%d.s8s)s(E>~Թf0۫n5 ?R H;}.-kak?01Zp?c:Rt""unV2H")9CN_ "Նʾil#)K=#٨=HpTg<KJEi`ARAӑbՎ^?nw!E":ruoQJƒCHW }_,%!fm{˳c4+ YOA_Ɩp-@fp-n]\rc6]gK& y}5K/YN,p# H}~?7ӻt~F=T?ysznӞW47]BRβJ~='OQ;}F|w#e[[e~QOi:nе⧖k%DO[kj뭰|fbP LU^u7¨R=Ymw|V-=:9s-/-s1~{i_].=DdCt>ʺ }O_-K"a<|zqi~#Oivs܋{u͛-#2 ?t;2=LmgyRcӹZQw=OZ\f?[G{z]7Z GqYfnSߢxnQosjly[N5+NAIBT}Xf0qzIH`n3iVѺO+yE0jNZX@Ey_ /^R {݊| /c7Ot[)`c͐z}M|wwcU~7QSڬsc˹KߓKoVqAk]~ŧWewӰ|2ki]]Βcj2ھD~ "mO9Z5yVL2t &?ED1vP= ܰl몹2J\,'t['Z_?.f28]KCv|g7I\} _i([p|fk,_mewlMW.ML][{.{Q{f/k$yQ5[IqqTT/Oqg!رt%[ {X4=0_Onu  yLT';=kEk1M2\̰.A`.@^@r|n5_'÷ym*[M7_`?_ik{tȿ%/kFFMQb{ 5Z boOɤr.4g}g^{mf#(_Qps۹ǪS>>aKPTc|+,1__p[}PWhc)ߧgz1M#ҖX{?ƚ60=ܨo^swsuuBq:w5ư 001w݆7NS ] [Z+4S7Gʱt?ێBn.Elqu]rʃmmhչiÍBf(Q-&"ۙ_kJpRNNpnM;u8- rp-vqiG-MoIXf HRCkoA FZ̿ٷj3@,AJ[7noiFlYwsJoN|uVF KE-+H|o]rN'\'Eg޷Ux-{5҉ ''~u.ݎwfD<6n4W9fS-ň 7:QӒzGуt$q5V(Iar. D| 5)bVj)Yu*Gғcܶθ}oͺUOL|6Z{Oom=3_Z'kgSGǎl{ѵ-}EO rWa'XD# '@D#h}̷O\4-2O 7JixM>ө|ޫk}]M>3ϧ4Kkn> ՛dN;[RN[/֟vhw8lz-ݶyVz-n=3Oۏ4Ol4{:iIzkO1ռW[ޚn|þiӺ[ݶ>+n>kR~ccm=Ogif)z=ekOEn=J=3Orn<^M'giM=OT4eglx枩4S8r'~BQ!/5%@NXW.ogny枹ɯUy{-U]*2* "v%U*>UP/ @}!!W䶞M6m䶞n>;dO?Ƕx㘉⛞s4x9gSq4Du4m)'љyf4K{~:gћlZۍ4d4M붟,kcۏ4Gcܶ=ǽm=sO~hi^9>ךL=SO4M> M=v5עiݚy&RwDŽϝ7<ַU\9r"!<-'VRⵎ%{-1y%c~ۏn' !#isDqԷ}[4f|3qw>[[q> nn/=q̧t4OGV&xMt3ܛ4n=٧kb{}{i>|m}?uWو $0. l` 330 a[`3333 lfm &mAm0&[`00-B00Ж`kvb$f$ff6-h@$!6 0فf@ HH   HI K1 03 &@`Ř $!mmfi$ m $ X I@! Fi$$[B@ ,Ā I IBɰ}~a~olp'7y|JZ>[i}ҚxM4D]uߺ9iyM<ki1䶧xOq$HI$ f-d{笞)U{N_nj`P2B LʄHD $h煙a7Js i:M:IeqUP;;D:  3RMzժzҝѫ_\7I`@j٧P "]j׫^^so;zK|4Ԫ7(yjU>.{v^9@I,TnYzւ!CF1 nBc)z3ZFe팮Zҕ-dH eeȯ" D}1 . eRy,@YRqF#UX,N4r:݃$|CCCa\#$=G.Ro !wg=Zk>.Yx E݉aXΞ! V ٯMP OH + pTo|~X1@2"cq""X7 ?TDT _4ڵgZ*XU@7>>w-nyٞC24vsP-C/q4Qd߆{IJS+Elc DD@#XyhGp0iug3KU>JU&Wp|#/fd:Dv|lXY8Q-5 xS: $XhdVy p rr/) F. 1@#jV_%늊*կUe(mpD4)k˼6ڡR!2UV6Sє7 f #%M+@;kY1wvvvC61J"|M]zׯd%⩋EA,d46ǑzYЄ,2ŵ,r,Zm!#.@+(("01x竣_o8x|Bh4^|x*\byxnT:bF2|͚2Z- 3\ 1f k[2ԅkOd.ᦚU:gnj`j܎I.@=W¸ߏJS!†aPR7ۅ!)zTn1o {S@],/#*zTV7R@G`(Q)h\i\fYeGϚEmDPvxȉAXmNJSR@H5#bNJtR 0T q5c)8u87ҶJXJ2x+XN#Y:F2 .[A{dnm}qRCmn  74+V d_|tCqu CqGI&ߏ*|!}A6hhu h8 6ӱB ׻?7~\J2!ޥky/z{o7ec>>^y^jf y&[luFej{teeߕ6q˗NriOǖ:axBQl5aFc<44H@X, sqpHtb/|\z 'V%g^^XݘVyfm h+ YrD`m:zU-/[{9lɑq !Z e[sd%&U3!c@ah-fY`eY*o7fv4!:3m!/V% K 3**RTcͅ[(Эex~ul+vX:2Qf3z%@ҫӛ_\*X ؈܀UT׮8 *IRRDl 1XD,,,1 cIm| r @U; ѧ8K^=M l[gkMpB{cU1``jK9)uk[s/ :=g__g_[ %vx^0(ÂB })aelmr۷>12u~zXmLFDcBE5BvϨ8*e}x߅VBv1x)'ĭe]&NmsQ˧\_GW4vDBEzU@3=c)BP}qӧenni={G6ݖ-T(dv`_b(ׂ! QY/3 fQ&z=W_;~5ݻt /q:( @'Y fD3 U*T@YU{"q^L 7 `NEV & NuC_1\`*,yW! E0DpQ( PDRpS 1%)TKbV ])S^.aĠ'jS [97Q;5rB\ʃl, rJ΁o%= f`48o \߳#+Z-x(cq0d z&8 F2'-×1-ݷvFfq}v^xw_xv=3o 1 T:nL+ZvhXj hj PPXXA҃^fjUW" EJ@zl i1huDMl׷dn5a$b̨q1ӵ +Lu􋇘 :ST.ndlQׯ8r˖l6c 5h*m s6k}5hcdB!$!&7UQER5TSfՖ4%S*U BDEHaAAGV%^cu0 d# $^<׻?1PU뵣&fɏ<L`)㔺S7e;p~cJWwax01s 4p 0b@40,s!*֋/u_t 5չNI(Ez Ќt(F{ӎ8\<.͖6ۮF1xn( +FּbB'2@E()j`'73dMYkyӌ-u6,x4PDE$ Eȫ,Fk'&V`sBN+4S*e BD4AZp.꺉QP1J=βQx`0bԶb cA84~*0]ذ4zܨMlw>=>mtmkp^4 0d3`6 0:Z_ ;i/Z`^$# ՃQ`0^AaP2!hac$.l±7MHf@ㅦ2 ֹؾc*X*QVr~0m\(QǏ˟ yy` 0:3 p6 aFe `p GhR4:J˭TDPH E@@3"#rZuc~.8W]5mjK}6%Nؾ.c|^6rc۫п*S΃ÞB10YlT5DBdV {oqf:5bry"ֶW6ܹ{ZUժhk; i/+sؖF-$[%|ő=yX+>(I͋؇^O1螕 P0DXAHD W|D V *f@DE*= 'lB#Ȉ8d1 A @,AG8N+bm!u M  HQDĀ}[Bg=5j̅*5p@cf@X.$5,G[iB#paRR(T ᄨH9R+$, 0~5Y9-Zv>a!dNƵ} pC#""" rY Ba<}o[r͉6[_9´\8rnQ1惚1e'kLT6;7vB )Â1bt\2(.>FI@2h|M}E9I"pɿBaKa1gY 1`HDHx 9D۪3@qll>dn[(zˇMP>:-oPk~Seu.iF rǟFi=;p 8 hp8 ; x D gr1 DJZ\Eҍ[m;;JN- YdM9D.ҮsetIMBV,KZ@(k{t2U>n4*4[c`jϰ,=Pd- x/ @HD(eH;9ҥVn$smQ仍,1=wsqtiJR`` сh1e qvqV^j0 b[z5ca]e~KeZ>׵/c?Ï{D| gg9o䛇˺B9z5C7C8o}hRQOc'WWkGBpsZ7ii? Qà 5Vo ja|"K r=ߔe㫋VO y9@/+G-t!!>$>[F?sx9ok/{s˵&GW҆|ly:guey,Pt"oG//\GLǪDǛjO~?n ݧY)sYu0?7۫>=ҡ9ݮܻvnT bjېG688Bn(l޴nrCrQ÷Cb En4uG#8p6;.!mn')8m8p$9vsô> c8{9sITjsjլwZǼ4XՒյ%0In4̚ 9)ْLZ/dGpbw:! 7y+7)!i&luhc{2)YE1cN*bԒ)➗#u'CXC߰Y%Иd@ddMcb!( %U`}b*W+-+}#K5ln2W̦3x;624م9t7˂iYb}yƚ6]լfמ]EoEϦ+s;˸?O'Pڿ"C: OX~ hD_gpmj>Y |ẗkCkXmyO^S!~/\肇(]j]oE]iؽ, /6~a͔-ynGYDVHia>,ư@p"yo06}Z5{` `Z;|m9ѧ-uky}&4fe"7C7M5 d `;ۈ`7$ذmuX%%G*3lg4m5Ǔ-sV5kƶXz\ˡL0ٮ.HL5S2w:{ יfM*4W"zbmMսXޥﱧ̚^7kVdT%XWTұZH._a)VRc.WBvwj"HX˶ @Dd.TxrjK-BJQ@N :UE #t*R9p$iD%@BH!@$%*APȊ `)4*@@R 4@E@@+QQT j)IRA  * EB@`ꚡQJpTUTE BRMRpI@B RHH%B*B ((J#@m(!%($DDNHT()$ AHRT9R*p#97mQRX`6 4R%'B TP( T@H7B@'I Q@HBd(J0%J)$PQ($`:"BRJ1U tTDPTTT)H2 *iRA I B$ꢪnT*n @`)Q!RQ*HVԐdQR'$NS$ Dd$ QR'*A H E*H)64Q!*lP7!$HQ R NpER*tUPRAHiHQҢ܄MSIԊH6 &R@ܑN$"Β@5b3b362d644e8fa3b359db57be0ff5de8a08365ce9a59cddc3205244a968231e-comps.xml000066400000000000000000000012471372027637700350560ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_02/repodata testgroup false true 1024 Test group Description of test group foobar testcategory 99 Test Category Test category testgroup 65c4f66e2808d328890505c3c2f13bb35a96f457d1c21a6346191c4dc07e6080-updateinfo.xml.gz000066400000000000000000000000671372027637700357570ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_02/repodataQ(K-*ϳU23P)-HI,I-чw4*705a58b0e169bf1d2ade8e4aacc515086644ce16cee971906f920c798c5b17d0-other.sqlite.bz2000066400000000000000000000026661372027637700361770ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_02/repodataBZh51AY&SY}22Ewk8P!BPl^jOITITjhyOd=OQjAdhiH<LC542SF xbz4ѦdhM2LC @2dFLFb144M4LFFL0(44SMOThM 44!zhCA&'MML0^c"ssʾ+Ҋ$)$Dt!B 0B8gT"q :G]3oH{Otl\|5/G޿-hv^ۿЦC+ kchN7 {tCF$z"<˜-bA.ݚ]syV eWi\=s8qO*M.!R~/, ↬ `1!ouf0KJ$jRaA`SDZ4 D4.~17; ҽd}G4 |FeW 0t~un s;tJɴ :) @~>n;CL9L%dBc7!u0ד{=;nYqS}YF)/),,${I>.ㆪJ;"(B? jk&DGf*DQ0ӳ3(`xx3 T"4Q5)Mr!r+l4 4$9@3@BE``'R\BS9i!(Ѐ8KѐI}PRil@jՅܚ1`BP,򗲪kmE""( ^CDڛ) t2$\ TV***/^" `DA$fʪ,_擄MLf'?LgEo+b/P)#IGc,vz˹;,(G)<3 S2wm1_͕33 _PI$i)ciJ ]3)I>LYU Kj)ł3#w -:CJy<:BwrL*LAjWC,f$c[8A`%ppE<4uN"(h7YOS>C 3u&ƪ0+\ʋ =.uDjTWèSAF;C0Q*`!זNJ[ ӤFș6M}Sd:.bf),c5ɚSZ 7˧@w]f1$#9.>$L7%zK xel؟$w)uz`JM .=Vp CEf @Nh ]J;vWgh"Aړ!Il 7ԺKHxGxtn]rvm!C2(lzcכ6mNv8zxdT0dp Iđe͢':TP~+&qZXLb!v0vd9FhƀLH$=Dc34 m5sho+vٷ!%TM4W]4 3$l&dsUpp>=ۑy>@i )JRBRFfi4"i4$e+H#$,@BE?w$S ,c395ae7d8a9117f4e81aa23e37fb9da9865b50917f5f701b50d422875bb0cb14-comps.xml.gz000066400000000000000000000005131372027637700353240ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_02/repodataR]O0}W\,a$1Jxɔnil;" oqƖ_h&SXZ2_m0#K>bWKmV}eaZG@ƔGa]J$(hTiJWHhH':U Vb&L=V[4eծ@LLӮlU6 Mond#Ovh=FU·5eȱPwpAZ;gZaS4S1^;T8̵9<2.!-{b}TP3u|ePc949d2b2371fab1a03d03b41057004caf1133a56e4c9236f63b3163ad358c941-pkgorigins.gz000066400000000000000000000002141372027637700352640ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_02/repodata٪PA!Dѵޥi!-4&z{ /|U'[4H#(k9&: bgF J*V*lf?R)S :@9 qq %>x 1355393568 binary-i386 r test 5a8e6bbb940b151103b3970a26e32b8965da9e90a798b1b80ee4325308149d8d b8d60e74c38b94f255c08c3fe5e10c166dcb52f2c4bfec6cae097a68fdd75e74 1355393567 956 3411 2431efa18b5de6bfddb87da2a526362108226752d46ef3a298cd4bf39ba16b1d afa4a01d7a692ab8105a39fed5535b5011f0c68de0efbc98f9d6ffea36de85fe 1355393567 43338 735112 76b2cfb04531a66e382f187e6a7c90905940d2b2f315b7fd738b839887d83c35 2169e09e2c6c91393d38866c501a8697d0a1d698dd3b1027969dc16d291d8915 1355393567 826 1934 a09c42730c03b0d5defa3fd9213794c49e9bafbc67acdd8d4e87a2adf30b8752 27b2200efa2c518e5dd5a59deb9ab33c2abca74cb74f5241e612b15931dcec37 1355393568 2649 23552 10 5b37f89f9f4474801ec5f23dc30d3d6cf9cf663cb75a6656aaa864a041836ffe 8239ecd9334a3bc4dfa9a242f7c4d545b08451a1ad468458e20f3d3f768652c3 1355393568 23038 200704 10 705a58b0e169bf1d2ade8e4aacc515086644ce16cee971906f920c798c5b17d0 916ca5e879387dc1da51b57266bda28a2569d1773ca6c8ea80abe99d9adb373e 1355393568 1462 8192 10 5b3b362d644e8fa3b359db57be0ff5de8a08365ce9a59cddc3205244a968231e 1355393567 679 c395ae7d8a9117f4e81aa23e37fb9da9865b50917f5f701b50d422875bb0cb14 5b3b362d644e8fa3b359db57be0ff5de8a08365ce9a59cddc3205244a968231e 1355393567 331 679 65c4f66e2808d328890505c3c2f13bb35a96f457d1c21a6346191c4dc07e6080 ded9c95e1b88197c906603b5d9693c579cb0afeade3bc7f8ec6cae06b962477d 1354188048 55 42 c949d2b2371fab1a03d03b41057004caf1133a56e4c9236f63b3163ad358c941 3928c6aadcfdff101f4482db68c0d07f5777b1c7ad8424e41358bc5e87b8465b 1355315696.84 140 364 26e351e1a38eb1524574e86ab130ea4db780aa1a4a8bb741d37595ed203f931c 0052b222add25fed094793c24e73aa07fd598f43f73c1643de26c5e81f6d8c07 1337937059 336 574 32d3307b672abf7356061912fa3dc9b54071c03a75c671111c1c8daf5ed1eb7e 1355335029.48 8a35a38aef926fd88f479f03a9a22e1ab7aa8bd1aeaa9d05cd696f101eee2846 300 492 librepo-1.12.1/tests/test_data/repo_yum_03/000077500000000000000000000000001372027637700205215ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_03/mirrorlist000066400000000000000000000001561372027637700226540ustar00rootroot00000000000000# lalalalalala http://127.0.0.1:5000/yum/static/01/ # lalala # xxx://127.0.0.1:5000/yum/static/01/ # coments librepo-1.12.1/tests/test_data/repo_yum_03/repodata/000077500000000000000000000000001372027637700223205ustar00rootroot00000000000000014663433bafc2d09e870a6156e6169a091939916ae4149e1a951159e56c46f1-primary.sqlite.bz2000066400000000000000000000711761372027637700357500ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_03/repodataBZh51AY&SYλou|_UT^d&n@@ @6h mvڶjZ֪w5 h>sXH-wh§v+^[q3VCf ."%3zRAQPGz'hd6,PKn^ݳ!e.M h $LP(4YPd)l(ndMhB/Z,tIDLV*UQb$DF*aRTEZ&$EE XETYbnY uFʘvlhba1@X҉L:*+i*3Z\wfxu՗`<94xz)zELua%ů]*arB=mSsNԷ6e l-b*G3{_S Q!I6b&vΉN0S7&ԁl\waU=L*s! x?^&ȿ<<2KMQc_o=B'O_߬H{d=GCS}W;x\ dk[kj*@D?Qm IG]aN/v;?W7B<]yGߛ~7x6ym(j3CultiC*A1xGЈ |p+Q$q^7kZᮆl[Kim-Yff[fR[Kim33 im-ff[Kim-[Kim-=f[Kim-2[Kio$tIfK_]#tCTIam-[Kim-{h[Lε?) kE[Kn2uf٘[Kn2v[Kim-ִ[2am-FfL;+Fe-9x{$dDgRWŒPPA`),P @ `W>h*@bZ#C37Qv2cwOu~>6m>6wŰ7J@Q{ΩLl{؊# N@ B n(ovw" `3A"Һ8`,@!'ݻqMkFfam>f3 imo?cZ1δ 8wptwpf[e^ϦS#oOc=5~c:|-5ަodH̔t.rkttֵٮvWgxZ֋im-\-}Vf[N<̖y]?ة$ߤY$=g?g-h̍kOG"Iߵ&"'>9/l,3vVWMkUZֵUkZUVUUUUVUUUUVZkUUUUUUUZUUVUXUV*‼NG=ড֣CI D@wsuw}S |n"8a0eI4YOFV(BeU mal`B SiFJ:INKim?O?֋l-x\R|H}2?wfdy ,D$D!%)MGo֥ N}H[Ki3g2ӗomfff[~[KikZ>3fay;wףy0I'$I -[Kim>3[OogkZ-WI~I'I!a$I%//I$D|JPu7EG>3!^D._QuJ|' ۱(^:Gb>7 ?%*H}H;!%!YI|>a×=.S䧋zS =Rնjƛ7 6=߀cdo{/bMm?^섚 -GK;+kk|.H,kҥO ("u(K~Ǜ]quQa5! Hq30=4ǟ0j 0$%J#f-3GO6d[!>wוo5| s>kx]mW9.O@EFZ&$EE XET%>[ܼoWg9dz C?ƹndlr)wM%^q7_rr徣fWB9/xysi83yWw:R[m>K_pUvgM6cd4X>W _u0)sYm0,`#Z4$0M`#bTGUGN߈:Y.1uVJN -Jde' Jb"%Q)PyZ{tJ:ӊ*7MS2\ܤ3{յmˍ{_?UFQ3NR3/?-ۢVjB\-!<=9mCϿDTZzxÇ,mVs@Xsǒ4c|dz4uU5s4NT)$ x9B>ǂ"o}BX2LTv*2S'b2(2DP F.$/mL8 ěKh"oTٕʉ0^ET o T?w wqq B&g|ΦV P@lDW[m?sQOޔQicj60w_osi-K}fI}ʥźScYvϋ_~i9W͊C22LGӛج>Zn)K6Gd3e¬A|/C ܊ A {l|&G7ȍ? ϊ1!BNu4 'Ox[ę-;HD rwBf7qLMrnDSE39&^nW-}ܙs3.#jZ=+u̖ K4Qg@^,~wRLnE;YڟۤuNMi Ĕ~ 5Ee8)3/>_/$=3S* ­fE^!=Saa,Lc$| :v҇Ka,hLeNK;409h .ϼc(ncc~p ԼNo(97MLAWf2B5! ::;!#'C2䪑e#bw$$y: ߕg%/%o~s6{Y ?~QGv IRNJ0 TRJ*TRJ*T               RL           *TS܃3Y͎uRc9;Y(!PUʍVXG@m2$\c@_"eX7sֱr psm^aa WmC>uRPM!8 uy2>k+I}NY;tՌ.*3q'Ǻ7\ Z0lwGs*ۗ@(]|.aW6_ļ^m6{]>Ž@oJ#~aAp.#/Wa``p>_#0?+JtJy}_w[I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I7p +.;wmǺ/EWFTHHIX|..= 닋+b+oW%mQWwkr~.(Ҿ}?7`]WXX, )yXQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWQEQQQQUe%%%%%%%%%%%%%%%%%%%%%%%!SӰ`ZjիVZjիVZjիVZjիVZl0Xbŋ,Xbŋ,Xbŋ,Xbŋ,X -ZjիVZjիVZjիVZjիVZj .\r˗.\r˗.\r˗.\r˗0`¶jյ3srBώ0`  0` 0`$,0aAoql5d# %0~.JՁa ׁW dfR@{ zoDyVsSFtݟ} ?/H[gGzgܳu}&(bhP edz{v5)W Y!/]AXQd|=V^RՔ,k ziz} (f(&mƆ7E-\S4s3seMFLxeg564Ԉ*+܌;&|ڮZ"' no#McE  N tٲ=)fwvȣEJ\G%8ԌȀH̆$$p9n q̬.z&nq:^Znp8H_ͮ)aӵ{\&FF/p-+ٷdͶcT`*Dz'/InNi'2zmCl::vfL2Ysstu GM&~}F hKϽϼ/|{+T2<7뿜ƣ\k\,2|يṁoזQQRa&=FMz2o6&rjzK uO1?!cʽ3JZMY~?ͯ[&/u~ XӀ(M%PcD SG$B_0e;jI澃59=]g.A}6&K[vǝCe9&έxp['S_eUp6`d&m~vlgE,w`g [`ܑg^ؙro {W 鍛w i/t>@hsF˶39F󁌃ܽq"9 Xܹ8F:-S&wϦQ !h÷ﴬy&)|mq0#W-,)Cr|6TQ]czykON+H掶__Wz=W>_uA~v$KJ2Qח~ǜ_ښasZum-9~+V)c_#hK_=TJ)c nI@@%霌+UPGO':!r`ZYK!0a0GJDX;|,Nɀ0`E^ y(I_ߔAIIyRhG}2 AX?m|?s>ʿO_my~;{_-UXӀ`<0%,P70MQJ(x@,@@"ʱ }<%_Η5?JIK~?`$ff"<4#bCbچ \ w{P/ ¤ IDz3Uތ_}=.62*7ʇL^q2H]2web|=+>[jd|D5P\T϶R";aхȠ_h^=L߇nH"˹s `%dkifqa3ǹbۑ@ȆG̗;32u-0:ɟ LjY=ii⏷C.N[pƞoG˱o-,%,1,DZ +u6i_ G#ڜKI4 _A/=iᕐm֠?le _>Z2z {$%(Hʗ_n11T1:n"ȡݯMeEc Q0 }9KȸhĘqWyDX\_'nqs|`q-A pG2aYB^ڮhy/υWQWl=f 73SWƗbfG}C݊Y_,63T;"B8ݰXǺ%3<`dMWyWpiJ X^dGs 8㝤 8Į6sVK*”O3ms.t~qAmpg#''YN/.?,bVBࠄ 3θ9%qѶDǃ+uLԊZT ,lƀ#&!FH*{%o?-JcąBF/&"qUfozvYjX|Oj/Br"2S-1̗{߬7%hECS&z=6{WL3~w/ BC0.(T> ewf|`82- 8䆵INqu'S"̠(CKa)l^>%ȌG F2x؊!f!&*kc9w l.~1&66F! lcp8gk,yd޸z1 cYea~Id,kNߣUC+'dl gK?!${m xAgPVʝ١%lpzHKG2%9}/^-<4~B~VsD*UkLlWX!,یNBŒ"G )i k#N6 L4$G^=Ud2$74{c9h ܵb$ɫS7_ ͰGyiUQ3KޑGl@aQYN}˟<ȯi(5ͬ#&4 EVQ`h /6~-[;J%lY&=2&U%8fPMniF7#9IVic'KwZK%NK>6<( _/QLQh/Mɘm8 ώ!~(xRٞL\& T/,] Dy @6l2_ѯʡp-/>yc-|ͯ%% D3wjܒ2"V1@8# rPPx#>W0|V[U0 p*5,\ 8ƑDA5iCU yFeMxLɍ3ϽSxP4nХ0]^K{ Ylҫx{O8Ys]8s u-\c*Ԋ}IW}fW#@]?kg(-Js0frG¬i`6}~ cU)!m:Vw~E ev-8ny.Uwl mֺEa{dezK ~ʺ/xW|Wv:񳮾aDi.}:~m7f.KdС- gk)'7 CY<rx H\u:T=~gϓ£CԞv;ŧŅR.@Pba[a{z{za}}}ofȨuM#}T;sJtg^ 0(tQ3 > ,:2-J8Iû/ܞ櫭Pp*(&7v3ybDjE&l*xCދR)^p C 0`MlOd@QP7הilR潌{% [kn-zq~o_ ].:+qrD(JٛY]\/?y8f" *=??_~?~ _-[g [ueN!wga8lg709t&29۶36tp1  %XLxg;z/x?wd C0p]w{۽niX1a~?}gW̬nnYq}Z eaݻQvՉjq0=?GVM_4M\a4)Bp2H,lV!D"k54gyzǓc02~J+ecKC]+c tԼZ1cgZص?{z7O~'UwL ~,;谸WV1/oOW>to!g|.YMh}=`Ȟ~_9Oxq?W;ꊨ({>"3<'Bۮ{?;qsr]s,ɶP1|?G}f8{FYc3wwp7*@tTeߵQbݏ_q=Ow_oex:l%:&9Ei1Zv!\A2t.q[!=FDZBV0ʀݵZ)l'n.)CA>1aCz\z (BihQIV6M_ԝ}&1AEU+w~Ex0c;GXgFMIqPMF*hL?>F 'ϭ#ZϦ\˚F\\hƚلU̍ Ʀz Z3ǫ? W?ٴmOf8hml+XSMycUC|gi eM=:\:ЉgebCb3ٱ.S|Yٙ8!Я'P <^ Άn.N.NC61U,|τzA}۪/h}ؚta5K2X &eˤcji-[-Z E@,/zI*5.DR H<)bd;.&pb%yDRcA0*Ҫ[][W-ڨd)S[V;iфf\I! D7( o.cm 83 fZW2⫙q̸\*0 R9$f#!-I'5-A HRH`6~?o}w!{y3#.* a4翷mM&fzgRCsI;:dcs[cCcw7_Ov-fNv;Kd_{Knu(}ᡃCC}1h{ t<Bx;]m䡾>-=\dL90IaFŬh\,7vp4:r*oԎq|8eGvb7sa:.+7f3@ϩ'Ǒw68b2l|ԁ~Wc:ꗓı!w$[Sls7m"rIbH45.Qjh\kNd/Q[v_o"FQ¢DR>RzN`n` 1w}_o;^riRu!-qȁ,O TOC"d_{v#1yNVvR'C:a SCo{#(jZ;C ŶgOv6G4\t >J"v5*weȺ궚)7::=;8cNq$_1{q^ X{z8'K:[Ǧju7Q{!t@)9#+O>چ́ ~f$CHIxw$+xGcWv:{ i֘ww:ҦhvlpǚCz@dڡnmݵ g"xH֖yݨQ7qu[ej.(j^ִXkz= (zejR"/jlMH &YXԚ{t*E/xf62L\EWsIx2lILvpxofɿ*N M4cMԠST5.Af 1 4V6BpZaĻK)&(ZL*A1jֵU1VN0A¢VzJRT&֕UJ&AcHIzڮqTCYS{%N[ljݻqm;^^6k5UY\(tB.ЯWK@:޺'fPìԇ$X#st=9stÏ/wC)O3T(\u=:n|s8 = 6d{%.gfogߡ:eiѯVuHh_3?_B{{OqvkE2Hf]?.v&ճΗV6hk,C,t1TUEQThN' Go#S>xlZt5H%HO// bfr(>$i^޿"zїA+%>5#A.b*D zs?lJCu;ݫ'd1$opk[OW];TmVm̘&:pfђ)h|1;b#f=ގoo#D$7Xm* Y;UEV"+zH@A `ITB%,? W=S6My6FgƲۈbͻeNoFϑN+DYRjճ1S9a`2+`J0Buˋ]ܥYاFrWwwb{U:70qnSB˹Vi$~D[T9ɜ97{݂ E=.ixKH=DWZЏWE^#l"AHB $6f.[m2KmfO{ަ0j͒v6f>u>vsf<m6`=ϡνuvܗhf.LigSVάۗjS&R J"= ^.5oq7Eo]hOjG=o\6JK9v5"nSM"xL`E( `(((`/`;ОLɸ(2Ai !(B0iMVx vgv+]6f 2 ˺*ad&NF,[[SraMʆ`0zὸN0w5jN!8^@B$*ތLDMz]ݗ %C5Yţ{lL!аT::?0Iaie TfE1wZov!rbSի`euhnS7)UJXOn=R&gn{յλ0A,)bSETQT,RI"g}O2gR)}Rnq d<'TPDOo^YŽ« ~Qy]]w􈍺koM|9c խk X__mENJ9hO<+ʉʊiBP:iCvѴmgq333m6jTۃch5I$I %Q%w{4ȓ"L,i^aJRENjKKKr嶶Fl6; iJ5X71Z﮺ͦIhѣFiiIMigYeYe?5*/'.ȂS+(5+˄ŗ%Tz !|]xʀ '1\C:ƹ귗6kї>?>X 7h]%hOܢp+׫=2=lsgLr>3cti4M6H:]UŭjّLbF ! ;{lDuΥ؇mϠi*ek?#?cq/qsk,JSOzf/n@!3_t|]=> Um6z\sm$F0J"#Nq^17}rͺo9W|7vØT,2 Ă5rů͏`qjk\r+=JOt2&B}l&4FDDA "&&LmlFL72Pd6;>/WKERqk]ۡNv^އmcD.z?K\!u{٨2mc iaj2MXOy0bZzm,*bݲUsYӋ6sOoGrqQp0X8TyS4.K0H "KkNqMk Oó ^Ɩڶ.lѲe+2[z݈f7:+X%gsVYqLR{>x)gbC}~Zb!?,@~6+M0bÈspa0ӄSqfDA3C Te.[7nB u.5'T˖вŃ/]x3^ߩ'Iē1 &J<70LnVlŇv A>ʓsŏ|H\1a¤ɲ^ ؾiʥǍwc úS]0鷟gs;;D>j_mjɬU,R2Į9sggEBZU~-UWZ*jV5BWuU(BAGg8B.׆8?/뚪'.+!zED$3[mSARRtW_~_w^αd.f IH&!&%>X犤!n vDYzܽsx؍$TIdz4pdŒm%3`%2z\!rs`0J' ̃\ RAt^:Q.&ʫt-%,&-5%A=a9Ȏ(ܻ;myn "鹄S=ßUsVj]bdqo.R,δa$[z.%ӔGIX6xʼnf+ץieQ޹luG&dEhI.1( 䂕NdjZ(cU;$~gc'GsҦHnGigXL,رw>ƽS2H$)qk/.YGK9l\>f˹Lnjf[,v͛Έ"5893}Mﱣ}>$VY޲)C]8Xd蓔v)NN+nIRnM[CGOxS1Q*Gv3w{om] `g4b1#;2'#SNiTx5WqA!DNHRF;*raneyUg+P(V<Y{DoLf#/Lc'4IMc0)S$̦GNJ^0y9"t+)drќJmRBqQ8kg^7 <n.p#e8q8qPg?&ƝE7n)ʰM|Y&Y7̞FFI}dr},HpH6EE^&2] beNMd+Bj.WGYC1:>=}Ϊs.m.qY ܳGv d\qnfnqhx¢’c$%sn]))Q䂭mWFmN*v<0l'k/`v77n~_;]h&@T;oHdSFApByS*M4&=}vf= Tq#. H".qD B}Rt C]!URIUTE $KM$U{{if䑒`͚`Eɽ*%b藩)ȽW8$zp6 eɌ7XlQ9 W]d#jqLmjk*^֦>](ot>}ܝ ڨ~thlj;>4t bz; T{**qnޣowMP%K_YGffl[s&ea5LTzuɂoa⪜e&;k["`z'OEwg~XT $W bA}Ǧ=8_jWثܠ\|ruGt;ٸ/3R`w_{I+ܝϝBI)gk9KeJJQ;ݘӱ)vJZ@r L݃-YV ZLVzhYꚶ:|i튒~̼IkU0dvҧE#4IeT774l6`Դ5ʐn<8iN+<'dbn%Cs!?Hf|kF4華e9n9xa1)-[&h>8OCAF@Ŵy4dmch$Oת JvpDApΒTUrn#Ȼr92Y˲`]ĒI&UHckWV-՟vvY[;gxhy/lʻ5HfIDh8$!5_W[=w33:UDZy&[:oq #keɑ[erȜv3v3*/d8F@bY] **RURuQ&Nqw̞rM3r^drh9eaRADHZe" Pprӑ}gǺ#Gcu\-cjp#YDG NC YK`~7Nͷ6Cp'VtŇL1 z%z%}$rs^/s]C'*j2j2j2K-,-,-j2aIlmRڌڌV%X^CneItfuldժYYR4-,)2Z-F[Jk"JDR)Bm-,ҭc1-)J3ZR[UQFB,Kk#!B)d)d)I@  R`Rڪ@94&b-YslI]CBǚdJ%TS&QB͌oR[f<CFqn=7JU,SKmqvjU!xT>Gb]6fb JT\~XѺ9$oZ)JQʭU]6zTmIFLk qlՍ~m~Fl<מ$3Fb.bM&/ի1CV`ͫ. afE*pU)KdKPqZ'||':I34`qf'WrH D[gxF oI+W>=py[; n0sDY:] ay&DhTS灾qMLM]&'t<169Jv(:ս+l*[W5椧}JgV`^x6hdN7'om7ozrțff(:d'IY4Zx":<>}[z{yF ŏr;ZIg9y5ٲgpsK{(]VʢN*=nF8Y$`l9[ZoOoE rW`I#ٚԏf3a)6SxчCb;nʝ4uEUEnpyEMQ.+2q30R98-Gۙ2v 2JDqP2UUZ%'nN7;Gf'a1LlU-DFs8 ”B !b /9 M1080Y<2BD "u0a` !$.) pidUYCWETxg~%F4wk[ &'M'^ʯjբ^kr 8 B$$ݵW^4S`wAq 0ǫgk ɽF$z06I0^}3*Fzd[aU1/D톶5)O}ɳwton o|)G920 !j A`(㠐HL&tuғVU'0G'ג{<^ї]߷I&2&^VkY'c3j!#Db.ʎݢ Dhэ\h٥c'şM,Da_f4Z煱tE$}ɝ dT"N,oKyjeb N͞)V7†˿>05`NMKZ5jmm_ձ1tnk s n&"0adZ`/_<;[JªdqNؕ1MQL*4Ii~k-Im*rUKlrY%{ԄC~:Mf=,qZMe>J%nLq]F&zjg ZKcڂtH0꽷žr^ߔs=:}t<p$L~ ~f=z$,=k{/e˽_H]M&3L=FL/EscBNͫ~GEOGP=iJZ`qV+$+VO(-n-nlAԃ>9!NtW97:cN jeb[l-+voQ#rS==U]š^]˦Nα.=e1':$eZԪf w%nkZ_E6hzwU\",]v]|0{sd:W :gY3ȯ_$VlJE\nٚe-:~Lj[Yr "w;cݶGMRc7Ifwns>ysѹ˃cf0yY%nl3:]nndT=vognŻrǂmYJa5Ȫʫc˒r׆/+I~4D5gV1m&:G]6m6dqiGσC^ 8=kʃ~VEEQE\Fg"u/,+cη}9?~Icu5TPPu[0=4}QJxLõΣY,.4ҭi;^$^6m@ ѴA 6L^!{ F"`|:PFq"G b)"B0$(~\Yi{I.I sjI-aK;g;T{j.HkmՍ`c@̇@C;$6q7閽u5L/J,BZq Zн.8N(O쎡F)BwA 7\_/~1bU|#4ȠG$BAR8[ZC3fhYe!#̇zִ^@@v4l[W: 7ߨ-cltmF8ņx?$FmC(}DQ &4Yli$]y2l]orr}NA Fs;&I$&>3M1 J(:) f&`['y6g59HD3M X$4Ja5oQ)T*&& $t6 ҀkZE5&ۀ+`r0`KLqDݦ nHUQSȣ{ŃNAoLR1npZIddQ*ʇ%j^ʫm8q%Di mX PjEtۜBSL'tFMP5TR6][  FJ sAc|հya Wgj,5,H[\1ՔwFAP {M+RR ##Һ9fQKN̐ĢNHJg0RBBD CER/l000ձ.8Z# [}\ZOҺ(yc_2l>bp}P㋵$lƣ\ sg@O9<9,h *){U*hI #_12@J모I#++ ۖQ 7ճZF/{|M%E^Y9cH Եƫ4K)A4D}4F֏J&#=2jl(FHM)\M4q1Kj**5 ^ֶ mzDqy{8kުd!FF%97ZddM/0/p{?"8P!VF}p찚(c>Ǵ9a1jֱ&1˒rŤnHC9<# UHy|Q4i,ILb|5!=\FLJY!Q)RLF͸ xmXw*Y`>SO|à(p:TTHIjr2+`H<8}ャԡ5 /ƕ..B,mZ/jx!4DEަéRi#@Ǐɳoԭ׍o]reQriŮ?7YD) nA[{㍸^Ð; Fk }k*ܑN$*s1a1f36da53154f18f2275e0c5da238b7cc5dfa20e95385f4b37605ee097efbb5-primary.xml.gz000066400000000000000000000225051372027637700360260ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_03/repodata}s8w Ywmn{#*tjT_S"UC2y&uT2ϞYc˹&~(8珞VL=;VUy"z ^/YyvRXi/Мe3: ߶Πſj^gg#xT~8ߩGBZU4J LiDe^?=ÚRd<_ƃ5Ť]V2Uyz>柢4(ͱxj٦^KPo֎9Q%Ҋ\ENNJ.*gg #W CQ}x(zg?;ٵ3~Lll7h>=_*jaȸ`>:6{ }%l\t}G<o-(9I@ؖm9}^}ǒIz`F`L FO?rL w=-v[F kO\`Lm%_Y䎌ޭ8^4z6Fuڣ=!`"*cDc]H+ş,;f<&Rcյ]|o߿{="5~hy-dK o_M$odjlKkFd/orw_,vf?7汅{vc/?nq`/?~_~yqKO;lSccx׆u]AZ6'A ]hRbDŽ$f 0M+&e9q帶b'ƥ6 ?ۤ}R@N~02։α"&7jU#ʹA36iCx!J^j?EyDaEmH:&JȚrۇ0axbʘRe@EsI#Mecl&Ag{~8.7z2uj dsq MBnq68xώtSo=Sq4˕ft= &rzwBάʋ.1+D^])ݴ07F#iGK (nCP>Fז54/x0y$V4o~zXT|D\vKLzkU6bMɍf\|h.8 qL#ﻌ{{^Ymȩo{zƸ˙Ih RiinJVSd}Æ6Ew. 2#۝᳑W?pvD/G) ˇ9Op)$U\dSoNuNj"?_HۤdBNCCb1i!$޴Z\b̳12@'o< 8l-[L{np,Y} ~#%p, B1!Qh{/|2߳bǤϵ9̱طl`'.A@ ڦgA@ -o}^~0hUі`}0i;t{@ f8rB`vN` rO` xO` <'0I]]!uv$5@SX 1%!gqLr؎t8Ԋ}G$o \t4.QAk~+FDZܛEB;֚q4HZxTpM]7;۱g5.imrhCqe(d0x ̸7y i(xBO QB5Y V 'k/J;(qUk^;;?)vSt͛b-LRI;:ve؍ql'[ՠ v 3f5Z$v/fX3,!֖aX+JxQ-Fh!:aw k&X'Yb}F%v4%v%V:.Qh4GaB )VhNamBOkjv(hjR|J#$Z$ZٰIº zaMºh-s~ +^; V!uճ gV+*lWͱYcƮ!oGG XͽfX̱x݇WUW^^c7vA9_u:b)ZtӉ^5ʷ.Y6a5:ozZy萾0)U}`+!1QٔI~Dh_¸ISm+Q饖iAL~VjJ9$YIZR)0p2(|*hZ2q3ZVqTp6Y! &4 M]Gi#gNfbgĉD\B?SJd2nj=2`woS!f,xF@jiAgձ1^U)+Ltrh;T&@X5H$I)lE>A,b W %Jl @͋@̙Q%sI)X8|̍J*3)L"NHþA|5hA^ Ą/ kh\TV](!^Q4Շ XW-.Y'uQ&U! |s.rx]cABEO>tUnuf 3|X4z `ږcn4ݏԒkqGQX>``.ݦj+@^,-ug4PZyu6[Zq" 8pp(2.b!TGrBq⻽!55 7CkEsGudx; H>dyJO)sV*"}>0NO\_h\g``:ӏSE)l;;Rح#omOրNtiESg&BkcJ,40CEVRϴMn9c`8baDAlPIv0>VzA~A7fPl|^gl2(ޖ ϨMU(*L -}c[>Z~7u,Gh~'v: /Wb=`:3U&c)xLJFMkG v 0t7%4xwyMe xVEߒ]sHºKkQb"[ܵ6^?11fca35b586d42540578e0b042508fff7cb0a8e3cb7c83330e386c891f66935d9-other.xml.gz000066400000000000000000000060061372027637700352050ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_03/repodataKsF(r y?;V\k=$,-HBI;dV][(d-zf7=O~[.wPyY uu#E\d?MTaK (EcbL]32 (a>Ju}_>H%؋ȚLe 5&o/xhJ11KZ;㪘ꉋ:Y)$Sv>۳Kv_\mR~)bVy<[1UT[ymﵡf'F$-M t6(bjR*\ΠmL̲~5ݙ %KNP3 iNF"F 7 GGH^}k::g78Gݔ][W͋c-->:@ڀ_~l3f694f7c23d07f5b436de790791d5262406dfbe9b47380f708fd2b2e9bb8aabc-other.xml.zck000066400000000000000000000115101372027637700356520ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_03/repodataZCK1v?iO|#[CmyRb@ms+.yڜqTr {dN8ya;&NiJҕ=Nâptu/ѵO{n!C- NNJp-ԡun1B +J#T/0Y~)Ir ihDu"94Z A{#;Îe`ڪm"$onmSUd F$㫹j60䁸j[kk~)ϟ=.RRjQRP"!-0up}m,(/ n=Rp #Hh!-a@,Fj\/r#wFQ]iSmF4%7f"7g$kƠ‹H= frSL)Pf(/` &A"0׭I$ CqR"dw05:<w4961}l-[~ֻ !{˱Wڻ7EDY0G# "Cr&n^S)Ѵ4cB XSHIkjo,ȌX8l`JbH"ZM7 c)8pb@h]P1 d=^و߶v[kr3_vn9gw9f7wW2. P" <!ZIP"Y````0. # 06!ANp%~TPaukcx0(/`pYN!@iPZҰnу1 ()6$$&Ã5.DHCl tMVaKqZnE% ;{_;wniZ^| 7G#àp"Vt7X`ddTT YW܇FLr̵@clx3fSwH݁ M"ꐤ C[.ۡ3{;KZ#ZCoy+eH;zpU$V*1YUW b" =Rir6h֢VO审`J/[eD[Jٯ-t_;ڵdl'+y7DSa24vrTmsƝ "z`u=Uk Z0"0+: 10-P̭05.@]@m8*`ukx0(/`J J!@k܃A_ @+ `&J>m*˞B2@GAby4ixU $ESTE|k^:dn 2O1*_R3 'p4gR!=@gDxxZǙ'z_LJRLUIR@gшA@bA%̋<[&ҚqcՊ[t[H'^,H#A(Pk1U;&md:8'=F罫Ҙ!dn t2WZsǚ vMw[kn۬S;2m&\@0gNO* < !ڂIP "Y````0n # 0F! 0?4TX]6L4(/ J#`1v~>VR]e];s,s,o#M͆j Pk."W?,i,6yu]]9Wx,| (/ #`A 0v 7$CO"H EgS9b>C ]D6 iP_$ |:gP!g,R.;*!)Ap|Q/w~F8Ůl/I| (/ #`1v`#cTu(ٕ `[i]"EA'ł(łlZmtnzBՑZ, l]o@8$<Û(e\vt[}SI]O%+?zϐ5Z >UrQmx(| (/ B !pz'J-jqCvB?K"J"%CR CRI(eŰ~ @L~ 37pF%^~0|B?˫Oc1$;:]%{5'[1Zן^lgC( WMB|(/`- E#0t[o'|AܯVN*a:;<Wє۝ zC摚 x<##DΜ{$y"EqE90~1(]ݦEVL)Dע-Ey-*Lُ@EEմͭU$N %emac_D⫭kQ'朄I_s{S'Q {}k4{/2֚e} {Tkޖ](X[b0 |`MW/c'=nEwE?A`b0 ~ u k5  py [k(1> ̭(/` b" m3†ho\5,Ψ^M 0XZ[ Oy7N, ҋqFcOABub~璣U㗯~ܭڻ_Įk cZ-vE<^ghPDP8c,%\ZhI9ZVT &9kJB4 tR1X}yn_٢Gܒ92kғ}-{S Ҧ9\PVz#g6.ӥ՞zULG[_f$bRm'rw1\?;3T:V.bq$'B 0$$JBa C H CF,oPJy&rs ښyܩΘϓWu$JʥxV]߲9A9npuHΈpJW0.;! ɞFȌpY S&8ZdhVKƀ]?`,5ܣ~v ;Cb ¡W-a^~r&' ^眢?1ɐUq_G)mx(/`R= T@ `gZ~ 7(Sgo A'579ΦXsVS/v핗,cdDNz#))̢W@wy_АyixN }M~# v cR2!M 42805b047adf8e89e5850204cc595d01f88eb2192c7368c8c6307e5351e51c05-filelists.xml.gz000066400000000000000000001433331372027637700356420ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_03/repodataĽ[s#9v.~~EGKJ'<{q_&q'[$$UUϷJJE`;foX@\6|X VaM|q;W:~E{eS^x,ӚNyz',wYu63:e_fXw˛uZ&/ 3-_~`I߀f_~٤E|@4_xQoݸབw/67 u"P8F` =mnǡҭ9_au$ww;%4:e1ytZW݉B!gGnd !'X)%zɊ.担H=CIwn^̃bvǏ6q;?!3as7$׫q7.ۑhȝr;fgːzm &.zy 1jxrnww41R[?C[m:ݓv,ڮsYؐ}T0m$_a9sqi6MU0OƏ(Ql7ܚAH+L# ۏx:t\\FxgaXmӷ8Mo0-a~yS;\uW0fXP,yL-݊a3JLY °,\gphϢsQJTZ|6s~?n_s/uqqHi._Pȿvr_]_X/k uBzMyMevq=]\..`׮buqD]S*Lפŕ"ty\\u..8ךrq]\\M..$אŕqqT\\%..Ԇkbpq\\-.|˽ŕ"oq}[\խ)rʸmE]bBm *B*E@Uq}[ly>w: Tʩ ӂT5JLWK * >%ƓNZt"(DTY3ɗc٘יݪβPe'PԧhԴT?9RQZY^By3 ۬9$-ק)\v}`Ii e4 K?R(j~F쫾짾fmA۴#y<]=nwȳ۶#m/x<}=ܾwȳ۸# ZOn~.i>F;s;N Vwȳ۽!n~[#yv {d yv;{乭gG yv{{d &yv{d8n,h?>uj>=zn'֘!n-gGV٭gCv .k=?+j?M ٙrrGr q^oiUUfʹ ~Y)X]qg$ _aaqgGϔ7?kyFoSJ3"y ^2.WE)sҵuk▙[v]B?y~YmXG-)-WqܺxxGP*~öj\{X۪vp8p^x-bVo%lN"_/X!o)L{3x毟|U3ϫj({M_`ک"ƪ 9taŚچek9ɳgRd՘no|2|,pNk\WĞ~aIh.dl`Qxvs0QWh!&.Ds|sF7H7ϷAݞ|zݻ}25 0 nE#ZX^ܧ7b[=_]ev &x5zlV %z""pїq~zw /M@X 9еXy/Zn V =VԢS=׀*akԧ!ԂakCU#&y% <\dЗx=nEza\~^޸0x^_"tlփM~ y|y]Lzn|8~_Jp@kg|ײ=k5cm_2h/GTjP4Zwow6U5|X U12s焭N0}m^&t7q^\g=k}u{x=.z\?cM5vYVoT{׎yV؄.VZCuီ󴺯ZL7VO7cZVCf}nR-v꣬vrZ̿U<mUGwv^@%P0/%oV {Ɍ-%|e,1_=ͼ]{( Zm \Vnu> 85Ɠ7Y}d~F6Ჷ//v>T|1UusH0quz>T{eNaz.V&%kWۡ?6U״Gt˵ x3#U KW;omGWkOqmqS_0uxLT$vq>y҉eY=ŌuZZV+ ZA_Vqm_XkjtpżGiݷ 3F\}pu kzVO3/,.lmY nμ't:[<@KwJۡvn^_L=zW~/mqLqໍkF'?Tw3/cqn…ۡ؎lOZi9hJoâH;j4Fd-Z 8k2& i4gJ֊N+i|IotKGMґPixId5$J,ka+.h5 BCml'`uG% ?1>)q,巌3h|?bpONͩom/Cs zϿ?Ja?o.=}~3#)ᲅ\D:TٔI'MHy U߿SE.Dr٬zL`2tJu8 ^kn _d'pT617..Ϙgc\[\? W7w "G;T"G˼7\ND A,^@LK4Ǹ~N??{ջ]˟y9|cx2c]۳M`vOs:'aO7ϛB_'miqxς?U(!خ螺2+f5:&ȠJʋg>I$bS4ScC>n2{[Gĉo>?Hz}FR505>/W CL[y^]ˏ<׭(˄ި=b/ET"9`%}lcY ɴV.A_:JNquK8~<܆|zx!ϟ[m|^)6w6x<ZSci>D)8/pVRs֐?ŝo)RgY R΃ovֽ'6&$d.wR^~E$*e<}Ͻ1*0s,m1OmCWOûazۚ"K8vOo?|zVq=Rx~PLr|f0g6i柾>mX# }{S/[t}Fݰ=Y3g09xLb~O'\ðoǷE{4A%3,λ{iYζ,FN(Ϋh$cιNtJ$i0sNl9=]rG]6 {f޲<-{,o9h+{+]<:ޢcv Iv ly/=E9*\6&-XxcLUs|xU x=QmAZ#=d~Ŝ X<; czgy"QK,h\YHS9y#BVk5Ru)]ݏn6nzSco=>kO.}ۦ]xق4 סzwj*NrKյ8_U$8a׾Dڔ]ލb~Cd ̲vH.ln;_ܜhtmXA9RTwK3b__z{}nGe<_\rο!̍=>s0# Mwco۬gosUP+pݫ;uwo1XNX|ngO&{9zf!RLqիb1[+;vh|zݚYYNxK?앍t^6%~!U\T~8>`,@+Lof2O);w㸍nH&G;lXiZIe_NE۩hVR+ꗭi nfaW3t}m?%6cu0nGoh5"ً+"vfDZ-m26WM{b5lO7n뮫Ph/j#xM)[745vlM?^[t0ۤapcm-ۏ:_ݧbדԛM+z״̰G\o\b\6k?ݐЛ6)>Պѡ}%\Ƃ"ߊ=?Ma=wC;ηS]=!~gá8 +t'uѺ9Ae{It`l%|)!z f7Uv\oxv1]q/_r;Mz֋y" >mR,?8=_~M9څo./Iez&ѤJpi+w[۔@)jct+Z/h;}IKk_Pn*m|fۇt+350NBri5Fmrnؽ&ޖ񣑙GuGz F:gN?}nj4Z2w.4Rl mo4Y7#QLStKސv4/hz*}k_ٌ08ΟK{m_R Nڱ6eZr'ؐA"rss9"^oh<5}9/\Г]HJ3#Ҽ p>5!߽pj\a15OnNߜ^N|Wؼmkn\78wzFxC!UKYyMØ6ЋO$u-5cv{|Cz3g-R=v3f36S?R}Jl1 -v~ iݨU-Ɇ>#"-[jDqL -۽[ sJSͩyZ4){:K+e>]Kza!V =tXCN\3jnV,O7cGP6с1mh(VٍeV׎G]fZ#]:N^Rmxl]6Əp!P|GB3f:ơد_nv 7b]hh~fFXRGo-e,m6fF^e|{橊C: ޼FDu"r$29g_6<ġMcHSjdjL݈SC!/6du&DQYO_pߒl;ʃ[ҝ#}, cpqo.|O uuӬcX? 5ߥ:tPea+ ȃw-Ö#f,n*'`;8v|X? - ׻%fG=vHm>=!low->ڱG_MZ}I[c!jj'Oov-~Ý-=/ .bfR^mVe!/6cv#5e!թ2s f 9^t'S_'B0RW{[-$"ܞu!s'&UKn wߤM!S#f,i;ݍ'ٖn7n|ZRsv;ő,ی/[4ȶcvXᾥ}&ٶŃ\\с[w|쾩 {R-EjUExM^CmV18PɛKl }Fڊf˩-_k^+"Maj{MnalZ@<°#u &c/'e}F +5Hlt8RIsmG=5U&>_5TJlt3}QsMW߽ӚbzI4zK!n<ܞ6#6!BpkQqNT5ў"aArn$%3Ud/xӎl#5nSwa[ۘ'Q\Fh#y{ӿq [gn =_֏/=}eվ{X"ged^ թDVļ*KB;sԪ Dބ|mY2KZؗ_?|ُ//~;mwvap>?'sK_M!?|R?OqƳl=kpl|0xgqK? swwEǢ(}N8;紵, Jh{˵eVw+ugxΊ|y 18~vRO?tg ƤHY<ӡS텷CZ3ez<|#"#nx^sgN}pOW!W|ݿ?O˷3?鼑 1rOp^:eo@ߘ+Yza0 Ply8BS3"a-32EF )UEB;"Ohu*ˬәB:He&Z9f8i'M $Y!`%1LN gcg9? QE~=~+*<ۿAVXÕgI)rY)h\ b[d@םǃ$FgiER-;(짂Hy^Zx۽ ov+ j-iq8xOf֎_ַvbzw춻ͻqmB:]ᾌ$;w+wLH>{L]ȸףz/ 7>;kvٯ>q0>6'y X ;o^SXbdEy/9vs z -1} o M 8KsoQ9F5/?N=r܎_OUUFudUVCw7]֠)fW,(AGL%._-no.W5A*7 u abk}j`27Hbn\JDaK uS]T \Y\H |G2 vp\Uz"T[Q$p3'\%30ݩ*Y.]n=@kZ9a/rk`STnYPm%6wP"Y~C` z u>W׉F 2!\ njfuHF&f~P kyjuMfvH,JR> 5ѡ [G|}HloKRD(/cn8ј )Ki$.'&4. zSϞE4*?>7ɻV X)fAV#m A6GOn+Zg:{}ΞRVe+ 롰 SX~ |vZlo*'X/b_.e{ 1]&k:/;RCT."P.= #3rgsEѤux3_m7E_oEm=+*ꡲ꡺j}=V@)x$P#GlT5bu[#XGld5uJs㊷hp|]+]xl}#+ာPB  8 +²iZsJJ\*URd?WI~7\#mâbU۶Gǩ۹R'pO Yp:*duUꄫ W!BV'\Hp'**V^!udw@VY!}dx@VY!= SZ*QCdi=J*qCkiQZ.@UNլ&uf5*PuVc5)POmpt0ś )yWK_!x"/|{&WKxnW(Kx_6fy:GD+2@Z [jB'\*T`D "XBeV*PUj< jf *q*q WcQj5F TZ= "pƾO7V>,|~Uq[y /|p|W|ᄆB c˞#+OgBL"]p Eȅ , 3t /X(T%~(ЇB~(B34/\y )_ eP B_X"([ϦʷݼycQ(病1βPr,uB9ZͲP,4E+P( V`U`[* /4\C vB@7ǺP7k)4֛eϏ21ȍK6 X8B-AB'-B-]!By+MhvB}^Hp mXvl Lqy!2u*,ĹpK*!Zu9mD B;T3WTB0{{-]F/:fԑAsKGjAIE]PAr:,k!5sYu`L+RjY rNy2<hPx e 3*iutZ2RQAB\FLb*4KXX}P1u3Ԓ3GbS$/> +XC -=xoM*L3@4RI3sP+[TDI*$>R9嵇>êj^R6eLH#zH'.&b4& f NA L@J.:8 `Xr)zY8q^ o0Y%mP&T:T)}" c,1":q㿀B:pt/xx;b{.# GBK KrᲪNd }!3r/l(26'y)3Aژ!~RzbS+ÿTZ C_(r:(1hJqʘK rfep\D4A/DB [ܺ@47/Ym{J͹!qӂ#i7DJy33g=ϖӋNSRj!9 sYdRk|MЂBL ,$HY:UN)Z߅ikY(eCP#Eֶs`u]zEsD0$V$i"e)_ e Ї,Bw}1P{B:)Y=s4 "G!#1y3T귌Gfc6 Ce(Ucaao ^xd)uY"Fq7,"JwO9qC98+$(JUAx bJƣ7:`}}HeX,z8N) "΋gXZ/T鱈 Z㘳RC%a`lƜ iMBy,cgS S`N(FSL._ewK0-GЉ0Jbr$1FArr1e] vy  q9L2$X9+t@(LY*vT9WtO  Lw ",zXH ΄gAk5 q;Pse8o) _]҅9{%Ldi C~mRDBx!cY2 +].qܐK+اJd kJa'TD. h:\Ot({':=iFl*ƳeZ!F]^$Qg` 9^9'1I{!\Zk(jPA9`LT-ܣCkd܂(. [IG#@b:X9Ut3& ?a}#p)s!-MlȂwTPQ&&{#2e`R9%km`=b2 Y9S!`^S̡F0*efM :LɥBș3=˽4k ~0Pa"fV qG,hմ}3%!"gw`ಹ>ھ׶P䔗$p'̀jW렂9*'+M bX  EOw$kE""%at4#C0=wHDw rLӾ8H!B#!Dd2Ah6Xێ Y%uCBtО(GC#8#752MBg,g'i0b 1c1 3U;C[w#`41F3e G G.B-m$3S#!.dZa{#0qh)!ݴ[Szhx IsOQt%xT lXL{$RR jCF;(fDSYS |T ?uA3NTFꏿ1\S69(N@mL'_:Ta,bBa4mYw4́xV& N:[c9պi/di9jrFt 0! F$Q9IJ50Pwɳ.52K@8"`'`i#QYq:@iFmTt~K{/SfAaU8j)=z1TBz+:a3"ERM$WH+QfҒ\&2hIm]r:_b&DPwX4S(!Dim&K=3\E $rXH(ͣS[:_aڤ.d2Sd,Al$䃸aޥB2eHVzjVƤlt++%&"I9t>Xk,AJ )>U. x$#QPl e0e2=-he<;9xd EEI/%p YO}p{0A z[/NeQd/$~a!)T-Ψͧ> ~ܔHpObXI:(4{{>#T9k0br gl$%1o2;axXtЫjiEd%* ~9H B.5;]t0:&4e刉:F D'C 8AS|ĵUHA`bg I(8/eþY+D2od;Iha"- 3 Z9^u E>%Qn99GmR3a19%tQW[E䪥9LaTyF65C p]s ٟ(R%=\W*& O`@Rt+q)s5]cE^"Ct'S'i"H8MgPi)бRja+fXZ{JI M1θx-(hIM2`Dࢃ Qa*Ed́#<0,,u# ,h_v!7eV8+"عHDxE \CŮ|Ƶ "33$ս1Q)!(> *((:nL`󞬮FgyeLFBP8R::QqB !REu-y: f;QsL/ܔTiSvP w\je3:Tɕ8-:>̤:}Q'C4HyWutÉ\@28 D×旼GZ#.a9r;$!o)u])s<V-Lyo#&F`HX;UCFG'uTs~I+ħ[d!)&MJ$Ir8bjcHTsH9^A}osTqQ0EBzH %bȭ|}J5Wä,J;8"(1`ɵЇȤL]7:emzYYKgN"V/lH`4BX*sG |PknIGeѡW O/8 E2gWp?MmH;$l^'Ug Ns3r ' )X*,HaHO$e@8(E8,p/l9g2NP@b08XbBὒ-Z^/Q j_CD48}/! E`]/='"0@5_2x 4^spY WB<}rv^g A3TA$:9:gVjna)#fY%pweLw%e CM^ߤD cKRCꫦFBȒy9&3 V춲 FBAO, Ԙ#*>AGΦsbWJ $Ю{HD9"uǻbDs fI8.+z]dUt0tn3#AжȬ f9RRD IRMZ kܳ8/=,:dUG ? $ l(:Mgp ܢ⡦>l<,;/ DJ)ӂq=° #) }+}AQZؒN;PeJe!B. s郜RБ,ў;RHZ7̅`3u.*˼>O۴pABUHfaaTpD y.*Bt=? ĩ(xVEDl ӔqӹN2  l0t@xHt |>)K h!!D@C*CKAyNJԴpCa$0xBKHƆ$FЂ.M1*nLDy:ݛp [D C.)`C2|!Jˆ x :G*gMD{Ȅ^1uJң B"(tXPA-#GHL7pAKV錐ZwTVd2bߞA |\/P3%z5Ȯ !z0D)o-ҷ(ʊ09 0NP& y``ܹTx!=B@#|pHiy !oꎘ6 S*psDGJm)[Aa[:`CVQ15X8:4IY-#BKU #e'a=iV'KO6 d ~ʈTGo3욕@$l1Wt!ZOe`XvAx}B(} N^)>"g%~vEI9sWI+7 Q=mE9t)t {R&"*xNWv@bRҡFZDJ>ϴH1OXO!іAi> V>4duJ|˙xhD]СgtgOޮ4DM=t^i 5"9 aZő E'ӎ([pX*[GwAh,kB ⠧6KJWQK'Y;JNZ4Rn,%^zPjo³;ìSE jjƘ# :m(=)OGH2DB8fcmixK9T*=.2|B1kSSP`KۨV-emELioB$T'(XYFTP.;8]Y/@?Lb: G<]Q*r in oG8!҄ˋE]pLأՇJ  C"Ӕm`+A(\FV?1$ k@+IkaTtߓ\]WS;~iaФCR^}̂pązx+#f!rL]%C<5R%wtR&.#)kԬz,%q0AQzSFG/VJIwYב즠F㠔7OGAȏU  N9B̈YwtC"{hW2 Ab֧`JBg.1 *m8]N?Ut ' \v)gtHpМ2Q۾#c\},g>Fzt!2zrtf]!v`/)1Z taP>҅Rǁ`XͤO0$gpAu@ Mq"M'ԜGXhC,ߕJ׈8K]Y"t)G(]d^ &tM=.C/甕Xtax.yL#?L utHf\G) s1VV:[J5fD:-6OOeJw2t x쐆9;U "(q`N&PHNҞ;$T!՞Say TVDگ0(nuC* >㤌TkP \ ғ8([T-fg]A CeX(E,mjkك=:G"]Yؙґ*:Jp#}RHu Dܫd :JŕŅL(A(]7`эEtiD#Hy'xxtFoLWY]a%ѽ!Q-xa.eK',Dݠ/EWaOtS+Dϑ!$Q "w֊#Z J_'@2(mٕtexQjo8KQVڕ&+#c/;+LH }(u% & 9͘RCP:3ӵe IDAN[ NvtqwÊ i;X`{XvץM.AVg[muA*,ԭ^> @8BjcG4]YNN" AVjeC,"iڪU̳XKݲŰtq X-,5gg:CQ)DgB: ?ig+LN<K$XNz蹫vfqRXλx&΁Q 7Ν:;H&hቔM<ތ8WQ7SJs2U/0_6uAHWgy fZTSr+Lk<@Ud?S~8/|VNMek/>c ;OZ &x G֤Q>.wĈ?tuÞq4/ȴ,7E~'"u"UPs6Y\.g+7,Ǧ&# <ui/ډ±liFB[AmMi)X_c+g*坡Z@I+Aͽ {Zk]aJW|H{N{aZh*_OY߈ȲKtc5E*akA'ܙRw*Y ;`mި[-;Mںn?f-Wc1oЈD>̆7=hůF$4In/a*G^遌jŨ2`9+W [ e=[k:kIWW۽X2Wd^ YGfq@]WeW=i<3VJħhO67-c~U4y#Ė2 &5 WVs~%g~9s~0)j6+5gRidoJH[Alf`;o4exj1<=E|!#Я\Zk4NvD+W]9B˅gFǷWs˲ l1>[NM5lPx{t*(aav=I, K\cg#H MQqǸ7R⹪m-UtGIpB!K6@$jB.qJiB?M xNY4 Q]k=mU 'F9l)JRUjIoC@Ҽ LuQ_'_>$]B43 )v|4ؖ$%,j;5Ӻ0t}UBnI jjiܠ>*ue 'S/0~E&oX5>٪(<*ް4eNrMG %ÜUI%Qs\sÒN@ߴXdZ6 'ÏJ=%E)s16 )IKՇC˺ &Kfnف)׌a_,kBZ" 3mC䯫vQ`6:'Dv;4-Y@ uAZRB˒w#.Ҁ*_$ p=(.Α$Jךv|/98s:a\dIpUFvJriJrh)BYWu YyIoK8\nʠl&eTCBӐ$Q_ Oݥ{5\բU\>8!"(e[^WYm%1L7K7}v Y;@[khAcpNc0Q? zձ嘢bo06=թÛ1mqwX}.(5B@Xrcz]:&R6ЙF+gj Ngݭerݨ&t\u cJ2wy\헶\LR* ҧt GU*sFoOu2cn` ځرQŧ%oN܏'әnv-A[mގ 4{ t|NEz?h$/uC-蚦&p7ESXCc3-Ju^b?I=,]1ld+pUg6-maʋ}$zT ^z M^]I<hSn&Õ9 ׆y_.ƿgθJ-:OnPozܖH#O-G\b̛^UWDzUѭw4E}Qg2~޺B2=W$HS)^ەUzsÝR/Y-)y-H?]s@>}5 b9˷r x#aMҽVÒsͽ5Fdr&EmUH$IQBw| &2>t*0Y^9[Kr2gj P]>ykKC2@D`׍K:p~;Zf=TUL~2칭dhV-RB׹y˛kZJag ف|'1 8I\$IڞTKOl˽=NnՖmo~hQIn<\0kنdΘj#!^cW:up%#QVʀ8/FUEj\QEt a%ӌS+4':^tIEr`!zK6X1P'z)j.P:!CF+wџ!«G`fy\Ա|֩gOc}TsNG+*RcΣf\;!%FݪUZe^6Da2)kij/m~]IU {y˩/Cʤj#8JcAw[Sdwy/E٬Sk5u~$V,`Ti:ZL`QU.ALnF'K[$E ]Zh9#2:2>oj&7F4rt#d|{])bъBL?i&`~'FYq$ λ Yf7_ K]Xm.3p9-dSwX-׍UuU` [m/o]ݓ-px9^;6 LS) j5Luw)X]3q: APݾɹXK_oZ~TߖhhNaldK:d (9 @$󎖭HC([\1ҧUnSAʵ̇KG^OlYΓNh/Y ]Ō>)ku Z3RX%y%[3V9X͔Xݠytn2""\;.g"7l`>+}~r$7K5UT]1%)ז{[f''`s!J=l?3O'w tO n>Q-;6̫z>1|c# amK{6/Enl#%AUv'7D'(zoPQwW/V2`r FCY'5w_ /K3se95mKfMmTr%S-=T?X7dۑ-wwmm(*ȺxJj{aC ;b jg'>MJr>z"{oѤڃ* ^Ra3{}Q#=IZFm]Ra*;Ӹ3a3>L(xPfv;mG]u EΟJ:t\/B2׳puqƕ/Wv;T2܅~Wefpodہ둮(lCn| sce4žhP %ǻI— Щr0WIW^5S7S|w8 x(U%JrI&)k)Lxg^vyAuDL_]F"xB{d'ըA>PSew ksGHVBTJd!& Gx_c>kēHrѓ,C /gkTwj=L`rUwtoK*8ԟD:>n&vuu4.̩Γs-:#$y`JJ]o^u9$ Z3"X~8`͟䛖Fԝ^*Qc;{:p<He[w":Hfa.bgbQ,c\/g8^~Y 潭Q{4[),Jʆ&{k|`!a5cKi;lm;r+*#wK 6¨3?@/Izef-VeyU@%ϖ WyO(5q ^{f:D-I*g޲ڜ=r %[.j>Qp&Y4+皌_&h,4H<*ku ag- I_wr։9 S렮M:owm0ooW `h#9Rl.^i]t\/(j,FWVE5#z}4ڥr<a³/W"I]_UeaC4h䅵I_)d0Kf^^ܡ_5tX;Wdϻr9upSrp[ZxɽFCo H9iIIHIyD :E|ҠOՌHQ%qK6C5 ]w8jYAW3- jq]Q8X ;L9R1 %!jO%ĐhATG#5#U-ۊ#MdTfPg@zĀ I,Hxd<yݛT簢ȏ6J]K{ls\{^8`MuH(Z7QMLT<~Ղ =G{}.2 k-nޖv%zEq=%3|g!챒663zXťM.YQYGXq!j$kN~"Ԟ֓N ̣`m fphϝd!$1& Tu2t`:y)-1ԓ{rtֵ70'p0Y\mE#ͅط F} B$j͊e|6(%>h@ ?}],[lW9sR ٖ%~,|,k,-˗;;:7fzYwAL%V;*uuL/ddiz`0Wu哌%5$ZE~8bn,3Yl4AMk{BZ n Ԩ)CzmջhbKY8>Szw{h"|XƬƜNd.]gM?O x>o$wOm$/x=mAKNn# IYc|ΫexTIPb@haߙ4@e,"AUgC{ CPռXf{a ‹nL6BgΪ"`;- k& }ڗQyB;[h37j vCebVyv&DMÝzΪVdߦ;A ,Vs%t)뒛}ЮnuPpٚv" =v%6Q2H՞L g+~8IutݺAÉ DYW8cSKsNnP9]MIyWX %炝} %oSSPW#x<ax`@l+2$qy$!IOVr* ꔒwD1KNM%e߶U*y%":/5I.6^Ro˱t皃D-]ZvPxhdlf0u9p"UƔtxQPz#͚ 9yUB NyH9ba Hf ]j~'>wmAD%ĒÀ<ԓI AUTف;aNSxri+v+ؒa .GϻuRYc񶔶 @[_? ◣^ojپޡqdts*u)QBSަZy?/j<7yDO27 Mg9nE٪6&IX\kR@n{dYC5$c{aɭxvvMVީ$3A eU5P@udhnjsIsL_;@yAO:@iNHm,^b3^TO|2 &Tpq&yAL߹ G#\k<^L kfx >Gz'w2~ody Z. Dnt#B`G2c>tA8Y*wF2YX [M3I*q{уoEbNzA'NE|#ԦYSΆ P}C8 ˓bdZʍY?S ̂'S\N+WZa~ w+dBla>.dW{/aڞq]2Lfy<mgŕR[ Q T=(\D Vohdj!UDe,~ʜW"n GmJ-\,糭$*({<2ivzr;㮝e5׋)G4I8_U_mm@^OȨ B1/,xs%\/'{oN"ݼ YlcMyQ&SH%od*ɻ/ry_5;cKe7D]^ !h8{oF&xALA_‹w-"gܳFE199*"\Y1XHÕ5C 5GidMbfg!ߩc@԰Tޖ JsţM+ älE(7]Hx'ysOH{yTl.)۽fJaetfb/2Ev(2|c9;XPD.U ̨Ry)=ďּ00kܼ%sɘ./zdjre<ey_U JeIq ViOm\[hSw8$"&}<1Yk_;לI -:MYWkD>9 RVzD؄u}8=K.v$qNU4X}X#'aWQdjhЩِͫ) ^5Ԥq$23]rn )ZX>a&ҭ6!+8uĚ= #ĔNԩ @Om[h%Zڵl,j$mf{{C3v?S58Guc)d 8SP`UcÙPWzrys%ޙ6+6"ۄ${%XtKitgvr׋_ ?>r55'AttRO;9ƃYݕ^P|\2AW!AO$XS{"{%^Y"&SLDh.:Cw¢$vރxN}yX ce?9 3̻gu&EǼGV}H==pk5tYK2rJzݢ$,Y.ax;>Xÿ%^-7$ guLfCO~лX&YpTjۥڮ2qLXIU+pi Duj(0"$J"F]sr=/}x1T͢fRM.'NK[USh{3\xMڊasr a^[%%ٽDOZ;y1UU{MN,&D/!L* Xjțe0!zuhWxISor^:wNu Jb&@ 8RBG=oKAWErF< (=xD/+:OYg}'dU=H^?+ɫ06ԯҜZ'_$>{7 %j1qdhS iĻDK Q!ą,o?Pm*Lpd4w8}.6eP0D2ڇ _|uF >ʰmeo%r9AN=3@-s7evvh:,Ls<'F(0 `ݾ`a^ U&(>ڞLNH/{>Ԟzޑjj)GN%µ1L +ƅUoJY͛CfµStKUmqa[λΔlGk5_[; -H`C6e5Q%Dt/^yS:ɋ1[~kK\-~xjE{bݧ ߳ADR羀.r`=yDnԜnoz[jy7Q:,X-R& UNp!jC";|ҍv0?9$Y)oMko03hнV v|:Мk|(^^J%G_uD"_2+]"fQOޫtyog PyVn#Iv쁜]9 ʒ#5S7gJ+T7Z,n +3Pe y1븊X(^1$rT P\|d5uD3\r*?C++rrc:_ *'޾=v _L@Z:"U.lD 0UMu]ub7>4٣H:r յ#]D")$Ag~>%|Rm3},&^]|F ǒ%3 0S p`my2=udD5|"ǗAGϞpսXbM6ԩ5B6<[Ho"8p2"B[rlZ.ɒ|JUBNW%xywV=Jη9{G4؇En5@35YŊ_lX&htڥOry)">"ei`~h*oV#̤z_i}Q*R)LoT!_)L{> G'{WkrV3n7Zz:nލ_[؀Y#CJKj|7/Rl}ҾVohV [%Oΐ-I 米Btr^ֺ|c; :}Od;O@V밸gKU^`{ۍFz v%G&q p ;w|u'0pVTUV=w!s;AzW+}$guԺ7p'w@mW*Dr}vPyyWTKf輱[ MBKƽ7I9kLNƃvOyU}4^5vuu1;qe-d:HGu#M׭TnA'6πƵ\tƻIM^O͙ku^v`:`[A mU$oɭAWlNV0s-F#A>~M[R[*2,рaC}!\YHzȪޑ@H >מZ|;"~j'"`lt-P4^) V{;Y-G rJB`#7aU2LgTŒv5CL6Sh>EG8kQ1?$vlsH#K0_[v2~09yлmޝp[8,.Iw?+5q6ui.پLxٞ6eŵAZ |;Jȃ&L$K녙[^_P)6`vBˍ/-^MA:$B7!<,C\h2yQddC4)l'|(:#֘i@9z@@!gh&۞}O[G ֣-^L+iK'w۫WE4{yN9wӝ 5 o>U`vu XBmjoW-y>^P1b=>쾛,yXρ/𲱻تֺAif>[骑6̚>tcxJ~eٰoN^gS6?s dAezrK ^eH *=@otz! 2xE/W>Ȣ^ `'O5Qӂ#Qu7ZDV@Kl8&O qVtGz&Y {m{*!Xۺ>M4_SIjV?Z[l2X+${f`! qpuC댒N30SͅyaPSa׼^>tˮV%i$_ޚaudۥcVjm$<|J^ՅMLlYeQsr6 5%`ύ\[Xn9Rd%hцZ"Xun.1u៬Rn"ِSҤVUnDW΁|/V HHxepv#V# !i$2xU-Bǧ[VM}>S:w.$=NPӞw}{Pa\t\kUI$Z2>,9 ]~-o3)+?tB"~ +n^ĸe`JKĸ[;vG-{}&ce*{y V-z k}& ('N.-aIkGU-U;G­IQE(X#l`i"r#d^pRHB^6K("Ԛvv'_f*B>Mo]{;_EUN¼|KV852D\5|*;38*kBa4Q|ep5tf[PɄp,sm0&`MER<Ï;gXetwۉ~Ý0gwP^_&SEJuK '$od:W\m2GlyY7I>{%p!T 6gT),eJ~eښmG;IOk9*-hHڎ^ܖ&G:{5`jΖK٫pUzgd *)Mh0撥nWvyÅԦ{IG-Da7)YzCn\*b呬xd'&!-Yd©BNB^M#I9'(,v lwBfT b_yuk7H %ƒy%U>I8yHJ; ysm3]7$*༆Җ7Bv1Z'UA74Y{%o?ZyYӲ6tJ=ft =%3)5ˬ|J\JmIw]mJTr Ck8>}.Vi%*AILIYA24NX⁧Kd799GW8R{Օ qN=!vݶ늲;1ڬbs\/e|3xQݥ63ēf΀-VӲ6n@fN,HU[>}q"7YGz:!(NbB:^\D#n%-WCfªy'RO#thgw5VvS@aR-<u[{MT>-cU=iJcz+jO./k78t 4rMԷIg#6ٔqG{4US^tKKw%.S:VL1nW&[o׿VͥmjlyiÑ#5D 5ӊoܽGA'}&)[ҿYlQi7?k)ZFeCgxMA0?6E|׈J^uIn#>Y;og)VXdJՄ]O#x복Q}SSj&I嶽}[U;PSDIkV^ը0P$hDа^)7\w%"ݢҵ4ɽ;'␣B2PVM6)"LPY30%x/BXUγ,#`^÷v6ݤ4b|U^^Ez$ݜȄG {ɗ;lp{*nGLoH^+-rzm4^vqa"f_+n"r}Mt5_b73)zAOAoo ab.uh$Xvy9O:~rco.٤YUx,UkUT^$~b9 QN:Y]*5]Vp6&H0vg7?x&QMKuD9؞AUyr~e6V'A1͖iYq 5ژ~v-Um' ɛǒH?I~oU)"2e-y65KyUl _ jK>l딚 OTyWMƯ]vx*2VH:3VZ @%=`eJ6p9 ^Ą@/jLmwaJ Gu!<#vU0%JT饩G`Bh(T6:a\NKcxIoFi,[ ltwH5l3:-V-x=19q|I\-  4n@5$5ZkW#Kpz]~8cm+R븱x펌[ń-G\"TYCT@{}Z`xYCz+ ^?!&S^=`@<}8$.V$vOutuoաN`F{˞q̳$<-h:YyB+%/?}n1<*w_VលsEГAFkJ@Z:K*WnJhl穡@\ҶLgG˫ͬ5WҸdFb 2 UH\F78Jj&$rGgsARU:F`cz[ A(bfRn$\}8K3ޭ:X~'v+@dw.`+-OcZ7} 5{GjS濵+Y=&0,zEYX"^)ڝ1.vZ zv<,¨&+V_`yxtcb6KnDyi%nuC6բr=0RX.6Xf}}<]<f,;O9'ji-Z=!~hӭjkGIb٫.$)oMuve眻=,Mz2)E/e_?_!~!Qu L%!:o6,R?(DtywRh(6:مs`WwY*: ʸ,b4PE e(8*) dROÚ}`جAJ7 _/kT&M08d wPfН)vcSfnb{c HTVia^M#u9/o#]w@t&3GpUO M "X3J ιx'/#$ӫM?Xo]؜)KHh򠩺1=Kn &/zzsl$Э"r RySϺS N|ӐY9Zd mŻtdL&7 sYdDnn%u.)Ud*> ū7p94 y@;Hq=\<_5Ȗݖ<67Ue€u-5D':;S7K~ף_9Dg,3)ˆ״vܞo /E .JO88jnE5oU%cQyex-o"etӊd4YjG*yrԚ T߭Yb BK!`|*WC:\LD⬎t1%67ȱ;n5nWù~V0Fk2R\&Hm:ŊO6[;0,ߊ*m{z&w]ka&(J) H*ɏ VP% ~q)KwYL'Qz@Q.u'ݝ8oZGw kp7Rבs`&x?p$oU̬Zh򾪚= ecyǁ~ d+Ba.5k-XQ;+U8 k:Sݐ{3mIk`exj+AxT'l5Aqv9UmdiPcaU4Sx]^*X0R.*{E|죂9ah{IjOq{R$,NGxd%5wVroaK/yebTʺ<[-(!&z[6"$fv:y_yUNUUVJ'%µ pc J?AFaݛRWr%#Xg .wDĂIZj.edA U!_\*(^5N.eY0[@Zü6$衍F(@ s-;+K22|Go|Y%`*.̦w jV^7P @Xέq(A$fPdi7{aLޏfzTK7my+WdmHsrx"QDmYgWK Ե7״>PEf5Y2)B)q1f._&`C֞ͺnJ`1{d# g 1EeE3ZuI{kqKm5 k٫ %8yGȗ<UmYdΌ"YEeftr6rj ,YZ*[t [&~$Z^.7ӇzmRl7}ٰFw,DcnŐVNV&V62#;#ENjCEb^3y—È}3򌕑ŗ̏keTz"ƚaeȎ`6]E,V&[b әG-;ezaME"dԑ-OIi>ߺsmͷOEC.&Nl紩qceIvǃ-<**$ןk=Uz,[/zkԑII9*[}gby^Gixf7z47O{[_h}?*1Q?mzhq \ "0rOimͼ/#aHY -JLfXDNGVݣ绪rm:8II&,(sWOIH[>kH} C@4L#S_N%?aL~ 13LtG5/=+YI'ϠM:Fm ZHIsLHk`ܒdqYL3h2Vk$׻]cmY%gmx,/E="bGՍm[ВMzzk|w.ޑhTvȾvlu1( ]y xIB.p׷/M㊵Ñ`_<ͪ@鴼!̹IO(G#sڎY0Qp1&`Eic1]aah!H(R8([q׼"A_%Ea , #LL[A,@W5ù3 2Nl,V|,VpO9gscAA'u#tRz=#O [xا׌t4);m\5kj2vʤi@@$a\oekhn`̜H\[vG9Cc|5y֑%oU[Ao/ZAc5ux8`'gA3EC#h+9xx%o|%Q~=>xȆ^xND9H̳1 䂠Wu@1+@!lo\ٛ>GqtԜQ;1⃚MGl4#zG%ZVY(ݲ8ݏ* TG6eBL8dEҫ l {:>EAn(sϥ|p%@<'PBrF!!Fa9WUD*a\$Ɔi cSd`R>I s0YP\_%BQV#XːC v#2*:ԾXlRy rWѽ:/fLۨ>/wpݽ"a(ٱL:&t%RUyMjO2(Dd;:`¸.Œ۫yld$ilE]. Ꞅ0Q]U( K$N(HiE%*C\Ԧ{A)5؏8O)5J{MjGFAoSq,LQjKu:<1LMgZlZQ l2c l_VFF0Y'in(S8&kI-8PP*\$`J;у\XgEItʼC7W ڞ.Ey^?u[: $D9U2z)#ӨlɛMژmhI"Oͩy"}5xr%A`C;hڡ?`W;}v0Ƣ)ζ(*}-m;1M3ј  >d6$f"3د=3د]f9 |8_>خ=\9 +^ h8F#X9 1RXUtZaX"5"GUxO5B]kLL׋DfT=p!ft,8]rz{Zn .kqٌfѴ0g .7z')LQx}J|”=\Ǽx 唷Md !Ds2%~Bȩs=60k^nD1爇 /ݕ_t<x٭x9 #h+` 2<%zKrpAVد1N(jo@PǸ=y׹,WLDHKtaNQrޛ˵+%&=3>bK]VdD K'bdS Cj|OM5LZkAShzMA& Ba*UYd^x$3pOƼ*s[tN\%;GhIX@/!:"Q0cL|&dGUU  $^/^lJr)I/zt%ANw +Q(PW R?$z|0= m(>v]UuZ[hnCt|rA`.@U~jfڠ ҂YDQk:qjkkkX*G jf(E +HJ²)<2%/)z)~;/XRNZ* OkeHquWpm8q-46lCj+lbХzH*0d#c۳p\qaІs}r% 8 t4# Qj`AR3>Ae{GBb<%EڼAW ZR[8H[qt8]^T_(VMq]+.˗%S0^$ edݳ Վ'l)CJ]8[* 8Y3N0ј: PU6Tێ ʻqE W$.MoP!ys~ F[Ulne/_J0*&"0gLR_xZ Y2^CtTdELaA;FLع>0HAI]D@P:k(b#謮 xB#d+g ŊrAsKV7FԆAhHh$F֖Z亄 ,'Sm!b?}S=C#mIk#v gh}vb1Q.X*m]pQsty:7eŽIդyFDzˋ(e%]~[)JxK/$7`6GIxď{}lc8, e Apph bQcr8F\ ۚ뺣bb^@=/1yO 0w<=úENۖ*w,/hAڢߐ\,A`n  ~ 3{yr5ц9qe:u vUt*k.\Љ#Xh0D1iNӍƻ6@4M`$a$!ߐZA!LkE Q`o: +Dҿ{(@n O=.TCV6RzSʿ:[& 锧 &|7u}n; [y?^=iHT=4ioX!ʷ+ލ]I;Kk4I!•Qţ +^("Leט˄*bc(V+ ̛"^5)~ІstM2ȃT\w` FWju# Zh@Ё.<t!bZTBt>9P(C(4OxQ`UrO'-nlUԿ %sPܕBBeY"W:eEYLB8.z |! ׀y0Q}28J è)wAP (?OAE%=EC6) {:';u`h:!2BLKy.  WRL /9AuQj֦6KuC!ʁ`W 6f]cĉ8|AO3``A{1N%4:o(lL<+pO>D+\˹^Qs-y>oym>[RzXS5 zm@<.5jt,&YsM$[gTXs5:\up5jBUjBZAu;&邡Y}0y 1/eBоe!1cE8B(g$NZyuKS;&]Z3!r|Ųu w0'#&ሁ(H.D< 2xŅ :},h02p5JvB7/\ ŸI"԰S 0mAj ~>N`s\!s$Z,:*(tA =]~ ~zPRF1 ;o!A[{AZPC>~5 Twufay ;Y|`›CBoܯhzNIni+XF;>"b@Yav.[eO*q\s] ް~=t̮Uy(K6z 6G=0ͩP4LE4 M3'=f?0K좁䪋UW?on*zs/y S R10HG<E hd < )PBz#.=h~~QNhz}12'*(E=QG[%)~}$պ&QLEC\߿yVױiƷ4h_l/nJoH}DnF]y_&lڮ1ud-=[au?<S~| qU_gjW)5z>gjR}YZZV@w1 ;eds'WṶjtzRTbxWM(/;d&_~A;x<Ǟ2WAVꇱij>ogC'm'h09ݔ57_^m_Ub v*~_RgSNFGTHJOϻi37'PߧwkNg@?#ɛ/rG~?=R?|;>/dQEz~N_Yp0OS<[ ^:Qe\$0[cQ&Mꑶق ]"aT +k%W c38qbh_74\^em}}RԱR}.ǜF6d8f.ŠT ͣm2*Jf x26 0KZeE+_v:r9`cIBJZYI#!+|=v+l6YY;vj(/ r=p5ߜP[hd*QPhF+͒B,_rL6zY)!_V^b$ ˜a YWq{ ,T(/`E.Pg3-/P";'xOPF-QQC~u ~ 7>oB1vHЩX94?1slHUzKJpA~'6~&Z(Aѡ?Y6-֛p%s \ 2KfI1lA^k'iEIѡM^l1$'"rfWgZ,P5`uP (/`E=b)`zC о"K Qf#:9d! 8BԸ\GʬI+y*kB}Wm4F8WRШ(H5}YZ. )ߡOz4mZڢq72.L7h~f wآ;kS7}.H BH >\*0tnqZBă(/`MUpyRTcX[JVUT)Y;BdMNL@fh9 )gexWbTB L)Q~;!{%yZNrm·N8< ?c7onz!W$ (C'@rA2t0Z$D WdDǭuqTSIJ`k{Qo5jvf2jBQY$;.\pKeNFOQJE0 mӋZ1[ЯC٠'Dc#,rJ9`O ٭|79:;V1:%+˃#@s%DB. 0j5@ um.6L£$}PjR@c^bJWX];DGJo7^aBw1)X]!L4(/`5Vx#0mSӜ\97 bSXt6ʈ*uko4 D'4,MqEєٱ:c$(ALP7Q-lSoa9uO u C,mo:FbIF xC,G8U4iQ&^/믖qHtk5djTntJ jiϙF Eeܺ8F ѾZ ܖ  󴙈*0FBƅm?=31*c9٢H׿~d*oCCBMDeUt=x/L]˸;g ˔ 99aӅk3b c1tm.11k9DʰΎ/Jie-tꌎz1kSSfV fg_RQJwWWz,-@Ӥ-j8MS錮PLhDf$,(20@A%A@E@(~\./zIy x}'wBwK,2"?LkW5y@@[ pLo<0KfzӴHD(8!ޤmgdF k4 D4Ry\ʎ*9DD处bC" q+#ՙ('֯ɡtH*4p{lV9HumBU}ٌ+|:'ܡ{z:Cl8<`b}R-gw\ho  0քZdb38|(/`aF{#0mSӜaekrn"7&`v6ʈ*xnoJ<${DP4E[DKESNf9@52Q=DaV{T7<ڙ(45{,"x&1> fLgj[EPFyHEhFRG._Ew2E)ɝΟ1c3L"[(C'L%vpn]m %tvh_ys-nKvy DSO#!681ҿ ]s-T&GL%W|;j"b+ޏ\Fޠ eOVq8Uϯ]mVS Ot 9 PHǺL|\%$ɳ & l@PɅ=Ld.!  .U_uirK' <̀܂K圭1sC`e&Ac?"aRL?rj/v$RF%{_"KT{rrBN!C K,uPD4ҙ028 1AC C@ga *w-,GZ3p*\4D7"_qRv^:G46alu`PomrnKcIYH%-J'(/`d[e9}Pl+X٭Rg(Ar$gӫikO9b߳1;u/`"BPd_rE{S,$ZOB!Y(H$%bz璀@,$x%!d<7"1ps L<0pF\0i8app €aa88FB(R")x,!r:_EV<HjNt}mtZҒPN9q̈́eVD+vƨQ3FØb\6o]Mn JwOtTݓ6n9V׽s;C}u:M2 J/DoNHwy!U ޚnvN;,jf|prZ]s*nsZ.tj-뇍E~%KVwYҿm4)U)~SaV)kw46S KhAx -=h)-=&!A^T]ޢKNZsJߙTJ!W%ů Sꜫ:9M뒥~͉FI6^ZߵYc .3[v }17,>y'L K(! E"Q@x0]\"A-gέ_Wk3ԼHt5|8Ah,ަ.Ď4YhOS5hHv4UN3S9{ ZBݿJs;/*VjK -(\>0ep nlw v-]L.ƌ k{EI|oXkeݙegcڧ}E=C6#>9ƴVZQ?/?MI0T/muz㨔%٘0laŬxRbP'OI~o_`ϴ'`ۯ&nCJ 6KMoSȮ@mGЯ)O\W0YLl޻x ,0)rr{_x,D(,L `E}̇ - 00 |sXȟyυBu&jR Kw.Axƴkvm սUˎ3ubP%5ͺӀZQSVN?t;wW6uXDGMaa\@0+afzJ#^R)Iw5j2|,H3` p%OB?/Dq^Rnk!_Q N![k7n q  Ssj*P:\- G< (/` A#0k4Eև5NW<E*Qꈠ*47>ʉo|ĩGf ?Z/RQf ֫yLp;1NΎiFgb kW=iI_1k(T Ƿ]Wj5QTyK|0 #g!h,0,\%~ۇb[c^1ITsRr>9:G;iqHC_=NPKT&<FKJ{6yn+aS**nd'5M!l#Dg. dgE 2i&Ԙ2~V# jeu>L &(/`E:' M<1QNö4Byx=OT ǝCQ6(vMIM%$$Zb% IPUZAZPli2݇J_A%^*1B*qI9-DQnFG;JJXj.tH$ov(,CtQJR q}z).1_Xo %ֹtC傺VC.'P;$HJr)i1bA1(r  PPx .|&)An%\ktV;*IkjVEi=0_3 OX _!_+&m>W $;yp a@ 'g@ !! QxBU@]ϝAW EN26Q:qvR'_RXQ; A`oA0,Y†OoȜٺu1OYҦ.'߬ɷy>ÜYrcKj])[ ̆lZ¬->|2൉ F6`-o#(|[ ݢ0f("ݎ˘*l"stK`鶢|E'CW̯@ WJ[iP@= [;曵Co[:l!فnLnQ'!FSI#J\Emu+eԚO!ܸ,YeCSMuY£)ko|:xӀgNtO<_RUuJ_J9M̳*=Qly6 nqB7m[{R ģdMlU~a;=reLt9pՅqVJO#{'en3;x|*_m~&Xeꃯ-|XF-d-Q\Q܃՗xQ9]V`~jެY3ԙr!}2Kh5Ն3OK8g/y~^0}ԕˣ \y\9OfK+EK`X<] >@?dW̗ņJkhwGyk'0gfOS܈sZRB*qukOZLtY Èьc IJRKa[vǑX1U7Z{#)R!XJ/$9KIw+g BvO# (GQH;8anɩVs`uRHR! Wr=Iq;)'KgE]F~(JWO61@p!q#Yy?|3%z ?Ș?=ǖ1hN],|][? \|ZkB}ַ՛E;<lcutma- 1^v~Yׄ|Ed̛O6McUG\N=J]߱4yQA>내.&u֕p{Xͷ?҈:}A]5ٶ rjst‚D;0uKhݵ`󈓴7;5rhp&iyvs$-p{Ӻq]zG˙DJߤ5Fޱ3#5+A8lRzjy;0 6x"O.7AeFNc]IшF>LIGO`ŭ-TT* (aOpuCdkDA_Vwpih]rb|[=VBX5Uz_Ϡ9 ҚQe\Ɨ7a-Lr$Jxޗ(P?:r DL-*f`4>R7(ёǟ )YΧȿث TV̊VVdf2-x/+P*DuXtGĈ/A,iKN7 חC#{QO!MK%W ۨ\KhdKF XI`IсemJgF'EŨh+E;@ U:C8zN)w2;c5T.GZ!ܓ/[ԞQUkꊇF*)5P3a3Yh9E%4)NS:dϟ}] !UڠZJE Mi9>h\Yfb̃;U2V1XuvK{R?6@>DCA&"<%/ YS x\{ 0lZBeа“~XBmzd$VPruNA46׺۾P`dZ&,M47 &'4 "d]KpW-ߢ2t*?ƘU,hR]E22ehi*~ž"":G"rWP"=dB۸5>ezΜa]S~ph4)E|x5c C4g]HxLY(*IaF 㗓b?wQBEp00 Imgd$Ȫ3"!x1=X#P"6ٮLHAm QbPjp;,F'eD*Np0fHBYX TV}Q:X4@bH@33*b<71bTxޭN3TmVX5[.³@:fg1 kO6]}؅,Q4īW >ջNfsZD᧵|)*V7rPW:qF"e;UDIFoy %A{@T2 M<\F\vI%q\g♅ŀA1) m9\gk"Uџ9!$@ACGX|q\䠜&IgX*:darnԃ0WѤS5~2'KkPV$cH;d>P;e^^/{NW$>35m-KVzm',P&N"mʵq~":@@h_037FA3vu@?sQCmF'ci2H-nd>& p.R~WȈ$awP sQbޏޞ<ѺL '-Sz评>}-Ϥnթ2*KQ׀'W"|NГK{U,GU˩k*݉IE >Tۗ(o7`]o|% p *zܤ躏 fæ!)#)?Jr%3H*px5$q*ۨ$Z꓎WCzƱg1Vxmi 7_.+UV9jh58WT󺦜)NB+ps mm'Q2O mϝ4N<*xPP%^$-חHnpƶ43w{jr0`+:+\ݣ:ߗxTpn |@u`ReS'cX%a6{dk戉`RnR2H `m`@(@O&*.wnT }'c}P=XPF^:9T 6G A9ڕ()9л<ǶK8lJOM,]t ,/Rep$kSxuʡ_JKo"'0{y>qL= o A8!ƫc?^[;!lKჄq9ɥ:%3'%}O.lΉخ8oOԈFᄍ1{ƋcDTyY{eIUA:iȼ>i::,ne6h\_w9_R+H?eIhCE8=(/$Cu!s?b#0$_/ϲD@.2~κBڅVPojǁA?tpHMq]'}#a -G|}1׭Aoq/OR&VqFs:4kZQTJd9bfZr*b' g(wwÚ5Vץo݇u&^GfmEB3XaĚ$H{?+u81's"d젨9:hi #BuYsp_-lꋊs[*T7DR4'6}ˬhEߋH$SdtTJQ E:'s0y+R.jB"or/C}&VW!"唎h"?ոԾd (63b+B9gȌ=+Jœ6%Gvű%зǜO)sϡ$a !H[p7UQ nhtM?\Fg ub0/D:#){A\("lRngB $4k67 H \鹒&Ѱt h% r JHy١TEbLIe|0\oɐP<S٠iUidRe^#=y]^ggcz.䀿N#jfjHnXh!4{m|2C` G{Tv|H}Fpmqtj U*#h;={I1ߞ<ҩ?Ux@ݞ/Qf7x!sl/LsHWy]e%K&x.?4wgkm.t~wT[J}]n+QqKk^@WZL9|yӬ h8)ڠjR(zOlGQ΢qx[%Dw"#V ~ H44 C=^]tl0:.yʓ/l ",_M4˭o}Gu].};k5pgB4oϞp; |ԏi,ߊ^C]y̸3 ^@'又DvBYJP85P_͆FnqDM(a8q4]jHk|Cv-{(M1ٟAêՌ8A";=b3q,VݻQ>vch 6i.ˉ.(o5ԭm&iʋ#6x*dQ##6(k5B%[yNCyv85A5Ni崳.hTKz"Ax=<k,2 iIgvsJF$Rza\ii_{`M;~L҂%%9% ;wPTj2A.b9BadTw&OnuHC-00 Ʉ;~H__Fa ?3L f82Sʀ!uPzHim oze 0d[7 )LJzyYTD=UNY]("?n,d&Q߭qT_џ3_]:b7%~#k+OXK.Bz.aHl%mNL3<{?UBPCВ~B'-,8kA-S:p^rLUiL OFzJlK6:RJ"bSjܑN+6bu*d(Q)KGn8ZD'In7HPi^3T rZdr5utDhL3Lb%M'A% ǶrJDਛX/x(2pxڽ>+KqDk9ɐN-Csalu.H*SY/1JxrIQ児̔I:d]m,Cn7uxH Z*q۷(ѵKYi.;Q&]0nTij}m)a\kd:b'@b!Hg@tst[ ָ]{߼D-++™ I3LnfL3м-B%~ܙfMxs0p $![];id!0|DKWytG`|BURٍ>~/ _篇8CKaP_hf"P~Χڍ"!Ka-چMv1K'C*, gPG"YS<'%YGaHZ9|4wV3%)$*y_=!^TQ_ -ߖno4gr5 iCwq2ydi{~O9)k!}k6A]?c[5lfӜԮW ~ CXhdj6HeÛhjgw&*b݁v(ZwH_g'Eyy(rY \k0HwZ[u6R94װ@͇vuCZU M$q2_z#-Ea;SC2c6h- WjսN"|XG.Ffqs<+̋%5W :Ty)]&[; w8ک-1F̢B;7 ꧽ\* -#d6 ӝA +Lz|َb>y&]z4ʅ@D` A?=iE8`AC3=oY:<'?e5|F:M]?,'cB}=-Þs73 A[WVL`8QϦ0J;4*u?-[q(k [K*"' +eRؾ+y68$"N̞!۰o[Z+2bg1 nQwtPx[y ;]OaA\(.hcH]D0Nl2Cq+H͝h G" *![hyndTn'~a` DA"3hjGB85)g#v+zsER_iFL[Sr+gs3# 7?, J Sn8Pua^=EdwE Bԅ1M֕Zj}/ P0F$f'pmk ǚw#mYhxa9ǒ5xԩ}2&T(PcrkڹT23&6ǡbU!$b+:kܽQ0W6={w/ǹIxd˰8)q0}%(AۉZVß! L'  @jtq/t3-#H .h<`. < *_ $h*4x T Lp@< 

hh .@/@0h0ST A,xAH ; $ ,(O0`>CGX`8@xHpv$`=18O<& *7`P &Xp(A}^p  hzOxD`.h`@ ;n r+T tXwr2ࠁs,L:Lt+dDxp]L8 HTB u` PK@ L@A02{D@Jop V 4 .@#xhGР60xAge`8`TG1! hˠ@"&hK4 4XxAPL` A8G A   <0Xࠁo @7`.;\ ȑЃ]dq{*\zCe`^` Pf+.|F[2/^}bk\/ԳH{3TH[#zE o}3F6s,.}2%Cγ% e5[ Zo:1\!nWGކ h5piO8':}!ݪş~0Ө=\}Z r=}5$-kL8N2~N(TP7^s%$fL:ٙR6w!WuM~IBzV?2ܯ6j Yo5V]gohȷSjZ^6mJ)> ZK ;fS=VU,—yX9Ҭ hm?6#\ a'}[nKXknõ8w+44ޘe9w /N #;q!BVy q;_R2ј\UЎ5'?ʗ~z}QUWM%m5Euې= ӕC>p.qռX,y!*#FT:ƕ\% ^t/r˞f:a!UM",t}DJދMCPyek(yWE|I.^i)k-{"'3 "h 4~98~]=y<]OaYvZ':aԠ+\p{]M9WBWW3BL}R.S(#\zw+Lt3JhyI1|~Y-oJkx|Yk7fK;[RU7AjqonhQ&w;d3Ca[O7WYO\"1VA۝zzi%t['Lh5Ќ~ӜmW%zxſ9S*OS}*nvߺtZwh7C`Gky-9t3 Jlz98y1M7VWV,8(+0L̪Z!Lܷ=%r>ZDj/^BeX -K2ePO ^Mx~cU̶r MTSI 窾D SABĴYB0Y!Ȋ=a֎߱^G-+x* '\P !ѿF;CgT]D7ZfHvB+ ]S EϠ ڝL<0-Xm{*HaBRz9 k ڼW? |pCS0'S#ikEc7faٟ[]-08%gwT, ˃%55:lZ Ь# ty}<9}1eV>nS2]Nn%qJu9}` l^XReL)"ӅG&ߗM@v_EnƄ[JRd^E%qBr!T]}%1r%_7寻pBg{M?3 Lq6;g!CuxӨWvSH=N<1tM2 q茼a-7'<ީ\F #?F(}oЯn}JjmޔJLsb\|B6\ TB3:q] &0Ƥ&=IXvIgBϜ7G1&B/oU)#YOg^/_"d&^ݗx˩MT#$(?6[^@o.$UlLF,A=hj{!!tysڃi?VTe'ۭq* {b]Jn\*.SM+3 u噘Q>^qV TFF溒dR{"V.o?jy(2<)4wM?'; @na[nv6wqi< BPSCPݛ#aw4 zi`~Y%lVH6I寧xlo!4_G-s# [yP mIt<e?<̜ȹWfv, ]=HaYvS|G*!f럝)":+w=bjʩr]Rj4Ra}\ *MFܞװjXH)\γñF+\[ȤkSM3 KL鼎OaK)$v ӝCV.-$ӎNZ|./}xc kE'Z):NIj|5+ tɣ@IK!B%:;#Crs Msꬩv?Z7O)V%h$u- -!q9mЂB ;&{|%\'y e齟V`cŤ;}4ZŔ |-)5n'1xqh듍 Բ՝-+`d`1Yᷴ2JKqӖC"F4/'t0k:m@?oB:Gp'J)])EhڕtEX|<7 S2a9!II0@S٭RpBi?v>mt8eP?*} έϔ ˔s-r |/NaPTQVrGMwZ{Dׇ̗?l7 -o֡}as6m5 Ԏ ψT!Oִf6Vu5'v'}*:o5,&=?Uǹ !"=]w4Hr`aIj@ؿq |!BbS=#20xϤ]\H$@P#HfKdۆI7qikNV'`ODl> ޡΠvI_LClU EEma~> lȩ˙߬UءI:Cӡ\,w̏c[䴴L@ a9N^r)t [b3a in ȁNx`sKԘi=m)hhg}iNH\`=䫛ԡ:qϓx'cz~o%̂CCWM Z#G6!R7r˽Wg=`Mg60k[qV~M/r| 6t5P{H RUzvff':Wc+1-Ci pqoݰ&ڽRO TThI\Q:-P,iQSZkiYͯIЫCq05`Ac %f6&6K񥉼,TCh VwSH .1+j62^?…Q)-hMj!W/Qc`-Rsui4_bʙߏT~%ZP߻ˁdmwJ&Bu`]<&<' ]:1 W1T!r7 DNCdsHkL6(q>M; apcUTj%2]JB*HL#"*(BLև6\(sBҝ_w(A،0j,VBc%uzG௬繾J{ ]L߄ c(ѤbW0wFtwt3'e%Z( 7t](1_삸hP gt3`ѶZ9tE5N>pm,gn-PtsTgm |_β&-L!!Lsx2hA|-MEib5 KIكP b* qFV ۈd>̤dDeJv/WB #v%^B `MI*7 RM+x,T~o2/B $|{]TBĒ} ^l$K<ﱝiV$E#>*:\[ƀB_ˢ2W iКV4ft#յ(zE䍂r{ }RX,IW8?'dP')D?NA*tR qwAe'w.AMT/.'I cAe>Tb2ҬL{Ml =%/ p1Cnpʆ0L) blګ#x(-_ty*F % r~9U2DXߛ%"жnɕx!iIrX4$F?"B#6) g+ΐG됚hxa>?= TILUUsᇀ:!LR]]jgI4 □cË~Kef${z k,6+xNL*cB%G|"=!:Ԇ!m'x{ڱ=rq匴BtkqlkB_'TsaZo7C*TՑ'P=wC;S]#TE䣇s™\l~H#" OWM &Gٿ6э7+M6M*\R޻lJC6 v6ңL5"nnBc I-/A85¨c u_ݙ[*3yȿJڽ=ZqpIkU&[S:rZo8UܧY$ކXڳ \9xyگ%s#Px7zxDWH F0b'?xSMqC8nS(39~N8!wB{cu#6;eP7E-/]i3Xpf B AwŠ.k]BxxlEt%n>C1Z'kFVEB?.7[Tzw Ϸ-Le՜9/Ed6#n"MesF߇iZm C:3 Bw/ztQ#&P{!l0NN؀!>KimA^B).0i,cXЊ ,*%$k7Dr=Οb H"D6HF\+A=BH4}X IRw2kĎ2U>ÒP =klqv*=YZ /- N}FwaŔn %nީ{_F>tP|un4+aZDˏhϗSU 7նf;R- lNDl\co+˞dhu\^1,; ;f]~sqM2:|*oy +>"8DMx:E6o8NDܩ7 !~aCY {BtۢЬ7:I"!Px!\2P)4BFLm7m!??r2,˫x[֫儁>zޢZ!z<^&r}[i`@xt|gH ӺySi IϣA\ڌ ;:CFݛ._Z f(G c,!K0ḻ&l(3AbԄ}dJ6Y:pDEp'djh='F@ʦ#}MmbYn 9I_W,[L !2ÁrdRe(B٣:Pq?I( t"ʵW H9힚TxȂߢoh)֧es,¦T'ei^Rܰ`=N_m@!~J^uB+gT>YTP#?~zv5{C麊O(? !<[&*tTCl :pZygt;c=kadEȇLSyB {'/gw&ޏNA"5!۰.|w d:!8<{ͨ ~ņ]-B%YV4;gʳkm mE%4pp.d^2ay§kh\>^c1~b(E -4Ur}۲ao$xgg&d`/cOP 306yanxڮP((S~|Ay Z9R>ٖ:}>䱦Y/ЄUkTt?MG>7n%SgfFPٙŗ Xa&R@vW9'5y-{*CPgG2lAc \R/lq0 KvQ{2Ngݢ PhLh/%״HO2j9VoRIN_i& IA?4G{zwi^p]qANQw֤Z;I3dqJgkj K_^#µ=2s=rƦ"43J^iÜ5>Dw̡ģqգ\ym+rP: NjgZb^gKQP˱,{Èpts$G49i ˺OUg Rd;6.n/iYo&u{ibԓJ,| wZى .=i=kly|R|uM2gtmQ#"l+?#TlSҩI=*rX.<^!eKUK/kWK8_ЋrGf-SQL M (\C[lʱ4+k|ΖC LM~I5UӯldIթky 2o^oc84η,ˌ]_>6.: V"bLgYU5nW^1?yY}$JZl1#c7 ʝ//_.́_4T3PX7oxnqsefP~gm[f8/q}M'}')J3t)q ~ ,{e2IsY=9kk 0Lo á]])9SWl?%8ͺava\zyId3ڈVVs1@ c|Xѐ6_ 50s Ǿq_mA,{%>MVi?h0[A^U88ͳQu;j،3+JߟEП K^8.:irz.wmޑ`g!Kh`kʂ0WO;zhYepm{̯Wpڇ@rSu;ɷv' ,LS4bhs5[A8mT=?OnjuٹtwKB(,X$_yHD2&N~֞۽*\]SDžro>~ɘt&-:ew7*sp7?GgO𯐏BmfNy_ ;tLDFverPT/zC}icQI֊= !S=8o;'>hs|:.RzA-F3ɞH0 |ٶvo_֥]y'"؋*׷=bFBB"&'JgZg'ug,<-Z̝sl5 DܕG%a 4, UźgjC'Ms7aE9F]R)iq6l-6}*[{"FfnQ3mm '8l xֵLƊx:*L NfyORq)*gmRLgp?`8Hk!XbK*:xtBb8ݳ?#z_J!wg-$+Nvګ GÆ$4蹞>=APtKg72B ͸M v>ʼnN%!o C-;%Ɋ/}˙EˤG "]-̜Y!`%NKfݸ$R@JլMrUaB3F'W4%\sLjMiBSh8 #jfZ&M\P|SRdozT0mqqF R/>ᝦR[Xm☗J@kbʨ$[hnW׊˿qt&*⣾~hpU4hm#w.=L 6q0F0')0f4ӁΜ<2/+H*XVh,2l{ c|њ6vf7gd'B3Y:\Ӟ0P5÷e *oj3Kܮ#OӉ]Y)Zot&C6IYKEF%TgV\bӢD/9F,cX mSt}\ ӳZ1n'ݴIB*G9bœb5qWa{Sw 92*%bOq|.Rw&%E!fߞ4M&RN kutɾsV{k"K$n ?$x'2QRD;~}Zch?!DʚkR?~=&*Rt1.ֺNy9aJWZMZ+`I^!Jj+YleJr ')h5ߪI@v,JӚEf?'E3+fnS t]֩51aAk*b}pfaчu2(6,3-ʌoIy3IZ-.Ȱ&j+Vy/gcRY\QذU&}5/?!iʞ_1LxA;Na V9d*9OM& ӈZA&x^D YRA߼ ۯw:LC7_; UUlFDy2#7iT%LօC WYMR%?,9 kU\P p6KG`@c.r{U:8hλYj#=UTh:(xi~XPy  ]D=(hb& XNIYM"QX5C.X +$~.3*!l:LQ@ݦG!$U1ZX̦􃉽=QX]pUUư7&{^L%98P:fE<35T(26#7h|pcbx* QMC*".D68#fYwAi` _lE7c({WSZTZ j㱛[B"w-5EB[0 @"09FhCp $+BB"P$p00Sa N|HYtT$@@;N]ؽD. ӜH5˙nX&%QILH14Wq]_nuEwrEqYx=o+e:wG)cZlnPמ-m Ωp~AI%4?Teq\tG9Hg͘ 請u(ٿ̕ Ѻhӎ9jLJ*kF[4Z\ ,[u9]ʡ%c0ao΅Fk ] 8;^H!\Äd6lr{՚+NI&i;2NiX'_ a}MRG2=ˬncsėJs7.!oYpn#j /)hWZu_dL$zM6'}jSrvj%W3r[=/8/KGpQ=8FTgVO DimGk =M3p{~XDŌFBBME[DMN$(7|ede $gy>.+FvPbD*0X<qs`ۃ0^2bQe$aX.D-UljL몐ocZúO քtZ]<0Hi>U]_(g0.+ZeE97mLzB19A ۆr%9<ў+?Bq->_䣗9_sךIqE^bB׈mytoaViR,t^O/kIyPD[\%BXo0j&(c٩-h MKadOoIA4`Hf:t21T "9~nf>#ژB1FՄ7Ey(7MMO& Q\_ ~0@\ʯ!A!tX0"mB$c +COC2{y&Lw@##.l`ZU`Qvfgp(O , ~wvn61&НҺl JS^e5R8B²(3G4w;4!y-72{錈:& ` +Kf~":,{ymPs)๥O0B~qwV'%3Mz"P 3¸Py>j1hHuU[~pAwEf*,28zR8҅}"Ir:!oWXs8*fsuBlеD}*ݎ˰O0XgƪMN]F1wۓ~ ">:~{W^/'<*quu/ޫZd4$ՔΪg (n<|d~N4n6%gSLJ}愡Nk^ޢƜFq<;^5(/3݊.tהj n찭ĿB=cymi3WQ.Abow?xtvw{u\JBcaךSAVP6v-cvVH 2[:p`,P#Xc'zanM(wɇE\Q"9t r;/g4Hp" lHlg Bj;I޳7:OI|*/ -|mBٖC^egN8 y w>DҔY͠k(v4,rt>pgTl4U͟Z⵨yCc_5Sˢ6J ޘNZ]u vڤF-,!8&'|װdRPy[%LMƫ{īI ,LeH9}/NF$nkMg8ȟbO9ۄ&⍊yߚ50Y {y(kL).-?жӕM8G>' *B>V&nXXv\Ts>^2NI2 8"C\L vCTR!9eOc BSykcigf=`u.j;v̡GX"9ceu\Fr%-O晍2}v#N[ khAvH&DcY@Kt/ &urgo,ޫ:RJhv0;Z^)IcmA3BL#* j{G[dyEH Ey1LWz@~Ċe&|$GF" jb`f4L ka]<<0$UdM*5⾊;2BSjv&~i jQKx]4h| b/;Wɂ4[h%(҈׵\^=F=є#7C :~B8`iE"*`*eU+Yn18C3M>@3 HAJlϲt /Y2Ģ(trq;+T<ҰIDX΍uX" -/&gQ֑wIs ?c~XR 1pbx?ӐNt Shދ˟X~-!zZ}.MX.@ K%i*^?2SwJD-[T)i1(^";ЏO^KY|Hjo z=\| c7הּ<݆B@J`u'ϩnKsT2y3.peAbA$~>[tڮi-g0;EH1;n. 1è-o7ن^YG:iBʍ8eES2'֫I1|S2𠻦pQzEil쑬cdMu{MȤH@\T]|ľ~ g~ܒ?}h;CV"~;qGAm57 YW?H?Jg ѯ&TY[_w1Na3:Ւ"0k&EDs`<١}KL| S;pq CVh,l}I jwLo!b*9Y{[d-l!Lz3!GѺFpBfEL .! 1iL :jJ*2skxc ѥG,^ <ܾ]$^! lۣ>Ig+lF2 laՌ?'InoqgJ/z!=+Eu^:\)s_3ܥMì@i4=5ou !yTȄ \_uՌg\HBfPr.L N"d"{}Op9Z4DN6W帻w02ߋMd]$9(IX {fGʄ%2w ,g)Ks41&܊4H0@@ nK؁`r6}Pwbs$:x:.hOX&Lp* MxmbRM6B@I{@ ,1Vc"+%SO @ff|ɑQ6]wfwcQjN|򺍇/eGQE%Z, pvVɋܪ`JϺ5<0D o^ӊdEQ1Tݡw?\IE)xSp~-;ɣvYBAqA5< o%Q@E^d?Z VTVmݙ爄"SNͨ൭(ANҥtL8bCBQR{_X{ r6LbU9֬ZnY@zDT}ah+#Jl{.& 㐩2OOct߹0Ej-N:4Q~1Q ?[ˣpӻ+ Qz;U|q&U-At$`ck' vXG!ƁZ_p!0`"wx׽"T&, w n(%IH{ث; ͮxF%i+ҫ+@krnc ]}%`f+ץƌvj$-:l9nͮzCivOr`ūZ>&j7_zM2Yi8gЅښQ3WK'Whh}33P?ɓ%5٣>z \2vmD[V:[+Q4wa\\YOrϏĎ Td-Z q+&jӗ7d/u4n1l@.ki"UmP).S6džkAU\m ӡYbɗg)tP304* J%sVohT9֗e|~ep{P$l6=ȠTS<^ao[ ևS8O(4ʺOsv/>?ߣTkUg!p0b Or tC&B2L~D](H! N5X] 4?`&ܴʅ~8Xn޽(gG-!z [~4XӜɔNL(g3Ƀe+u/Qd;!)o =r7xx\)Pm5$10ern"'q1CӮ(>3>&GE^ox{A#&v%B(IB'8+w:x{SirA06FYpwR+Go]2B+{뉚:m(b%LS|-[ѐoaAKcЌ>l9s[ PK"śU2PKݓȑ ׋iX,rl&g:)"엔B(JA1zR_('`jhߊqh( ^|}=sG(BT3t/4a@WFUBA⊎Uxq4V$|8S:jsR4R̻ NO<bN B™7AC*\A._wKĆԄRӌ4Pe2lL!,\-ߜD2/@xxO: [F]UW,SQ{|vM#|6Kɏ3L5ݰpሰo0H>ݳ'XĆq:2f#X|x]@ۛ91!<xӘ].#W!QIp*LWJ1 j/JE}Hw5~ᮨ:jfO:B]=bϥ2֜f~'P})|HJy>DJ[s"|3*@ݠ()q Nu!f&>Ws=JWh|8vGwx ,/UiO=f˄yQ;D3GэG+]lwMf`ծwP/}kg~ ,*Pp=*AAޞ`RAABsGZť3X H'gb{Oh$Ґ#5by:ʒ0~rOy{}UK%`R8>XJHi,OWZlE)I +qSz@`3 .HGŽ* .xjٺljsnG r"4z:bk{Ga7nmW+$L ;' (C ^h:Gڱ@P "WSLrc8t93|wKq:9] pz\w\M쉶 hLOVІ0 :|2  {끸m"NC#f{6KU@x| 𖜫G$q@[@iͿ-MYIoIg wEHT]iICOû|t{ SPg):Gk Y386^,:Tޑ.^zUՊƅS"ցKv+%J*^ݨNnrbH=Q֫qыEDy{Fg~qsfJU=" ̦fWY"IDm1}*8* B3cJ: BQȥ{bI?)-r>YLd7:i߼-c{M _ E[aבxBNJ<=Q &6Ooآį~>đ^[+,L#^9|Wۥvkx%NbM XN8r8US8 d2[ AUhs?_wkHߧ wU>BSἠUajf9N&iS|XsG?uT6sߏCޘs-}nu{2wܹ*r!hyZ*\ul} Uh!FGfz^QlcUԕn *1ɪ?NJ+č}Ԥ>vbQo7XƦQ(;&ҋjckL^*:uI%-6Ev!rF({'&BcE}w^ !R3]Gja]#7$.acH'ⴥE e4B#;tÆ&b! .: ӥV&EyNYVLc:SVh4|MΝyj}d)ctRer!~'Xțj'p/E 7qjnl6}}"̋i\q/hbq4sIt㖩e뾽'b9SVv*Y@(?x.UpٿML}Srk>.}2p˧z5f)@oVCaskv,h{ۿL?DʞOlVrpˤceBrA<|$X]VFxo񸭭:e.chWmH3NƏ3%&.E QK=twNr |>{uc(_s}fš.)mglX̴ҹ!sԵf>x!N6_VMZ?.5װɅαXP|"\\x} 'll1{kXN8RLr5ǰ'31|D~yanҶa6({ܳ彘aXl{"YQK/Qe(vV8?T}UMUWQue#2#;rI{w!>Ģs* aiR6CGg8j0"/DH{1QՆ CRK1ɫ5(2p@4H~Qi|Бի0_шWT8U-L(o:/u&˵zf.kpEt!%ػrEn^i}Υ^dKyʎ{y5%h0@hH u P@f8,` P`e68@V8(81^8  0p``vf& $ $H A $Hp)8>P p0p` ,Pz 0 ,x(aA}ဃP \ P8($h'   TN,@PPPPPPPHP $Є$`Po n@  8X@(P@9W   (|2VUCA440  :pW`0PXZpW]w___8; eHN80h@  bxM8H48.hvkkvwkv+Ph8 H8`0p(`p@P8( $ p+V( ``"0(@@h@eiʈ`:CfwߝUQw1F"ޝ Em80q0HX `7 xatn.*-P:jKm;:  j ޼ Z^{Kܳk&(bF!Faz aLDJIBĀBAA 0bΌ-HotXL2{I1(60T_E `z ? 2QfEn{fsz2o=qIi[iiE|Ysi2&".m ,w AB«dN15r뿖czƪ,'Hخ sk$9%_|yzi" u((U?4 9 OF x9ۨA/O^y'%̊CkXuJ;ԁz$B$Ca}eAm.JT9yv횫b2<'2xy6=poږR6M5"j% H6r pHB6 W_y؋C R/)G|(մ>f-^]Qj/ܺ~tSE2Qh_] Uem! O!pCMܪ?K#OuTKǪFrRP,x&jǟîT/IA)f&݉To̶.,hv ܖIc9Հߙ)cbounupI )LƺPb*\J1@+`[kdYTFT&uf80 (=DIH{;|e'HD(o௉|>*tS K2,48$1\z~A S~"= VX,egL6^c4<]S#zQ,P]g$,F,',1MHqԎBedp*~O=MOe.G\қ=!Yk$WOĸ*+᪦ &b ^ڞh7d2_vaV*|q(MX(E㭴v(?bodC**`8b AO,i(*.X gZߎةtL7uEN Bٻ*ho'g=Y>p KPRc= (<4 +cMl0 ?/FTa+qScQk7;ۖ.v*'^;V"rSp!5nN˥CFPvPs.稺LOOAae'GiPlY`Np%a ꍐXq3-" DXN=JzO[ .ye XqtSۯJк*EMɔ/11t:H(FgãH&Z: ՛tG7@y&{ +JA;hDhʥU;$ Jbwy0[U;5X"#S!Lut?o ƥȍ+7"ztT^WlzJldHp+`cM;sJa   ̠e1P͈\`4g{!]m:!/3QdfvZP.=H|}qhpH [8ARVhZX@UwݠDW琄T8SQ@lJI9[ ?:|w9ZSX y+}c2z:B9u6e zOx9z (7ZCt܈$aX?)udj e:PF%*Nae V%*{qJ"OͱJG3 ?'1TɺXu(he#_Ucj"bkK$=2>(J /OfHkq]4د`8!]~j3 ^aU8&,N~*OemE7[t9iHڕGWJW$;aN'0%sq@Yå !"H{_C; $!;΅M="Xv߭Ăg%}sì߶*K4,mvŐ ^u0pHH7-hNk9Ud8X%X$6/^nsH^KM1Qg",(MĈ:qn&`TFy#eԑҔǾ^gUϧQSG^`2Ң3c;G:ۿa ڢP e /Sd.-?C1ǞlԒ J+ +UFPZƇT7|ŒC@d*5;2Oi)koG Tғdou8%521QMM*RhYLgB5'2Xs)ƃ0K@mgďv; [sgo08EnOAf&!~TĻmO !@{.D1oAԿ=k] ("o jvI/\P(1gS$fI;g*/BDNB8zZ3~<4^RdBeو9%hHi.@ QS ċ 4y mI!ZnrDvɞH'ޞWSja8,Y$h]h4쪾Ÿ̵a;z_A-xRy ZNjbwgdQJVHme:ֈv,Ӊy?Ȇ˔a\Ƀ.no!.&ף+@Y eC~H ,:zkaU "1!՘k ݰύJB|5R5+^6KeEUzLI"EV:R6dW:\naTs Z 3TzVq|foj< " Sbbu{ 94dwOQіe QAk곪oa%|稉tQ t@G7 D 8jbZ2E*5ʼn2&=Srl6N$qz#7dA -iK6H !kN—2fMlyS99z*b5Q(˛w9l%6n0鑗,dtE g+˦Yb,q{% qLM2ivm"9PYm؉zhUo',8ÅA5 D+9"Cj/F5$j!> %g (tD^2]Y)h0(MY8#,<8k-LA h&縺JG0BUf@Sgqx!epGاhBm4`);6ܠ]qV n$ 했M3kvqsD+[ gWd|6F6{yȕt0]3pc[[' {bGM^]h4^o20a[nTR@Oax[&vHf_ +:@ry g(&>FOqa Cmp ގGiT'd2ݦG2y0azq;Rݲ ҔGCm%Cnc.įtSk0јX[GVD@o-/cv'F ycDJ*[حjcaX=Ԁn $H;/(/`e-!@hɵI(D][BVc$2č?F- @jبF[0r7q#+̘ Q)9҅{\&]M# ?;BH%'ox|Dڼ˔dF׻cNi|-cYV"s +5pb;q 6_ӌ"X&#p eݭq~ THiF;0hէںf81166ac5f4e2b7cff67fa5da3b8e8d6796f705dea6fdd426519e118d6421896d-filelists.sqlite.bz2000066400000000000000000001374371372027637700372000ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_03/repodataBZh51AY&SY+/ig`"wq5 z |f LK•lw}تPBJy)JJJ{lچ40PѢ*IQ$V$I60PH$*%PITPR-٪J)(*G{ ( P)RPRmaB PP$0UF|PRASYͳ>@_qH(P7UfMN[vI((ݖ&fzWָ{ %$J#V>ݰfmTSFMӦr]4׻IUb̤&릡BRhPFA%F*!EU# %QEdB^UQA*nI((lp84DMѦ &!0@ѦM&@jzhiG0ɦ4CFОIj6MB*~`E)()$Ї)=M4 diѐ2i`@d 4i 4444 CU?4$OM0oR & р0ɀ0`M!252$fi@dhM$ MMM2OQ hh4 4$  d4O=Qh4hzih6MGeE>FpUB0XC)CLqL >J \Bqd#JE{iU"D5|d%6YRmiz]6P f""61vs&@h IR'(#Q؄d@i/A婽&3,Ө; 4ET r"ο=_y;Ǜwo6$*z5=,z.y?jjdm/-sܮ׵{>cO Zjjjۻk^W|~Et|&RӜ'*WM}UP - r~}GIАC}!n U=2"ev2qYyZ,u i[ ^ðl M h(J"n 6IAש$燔/~O{(Z|Xq> 'z03&eDGfJvauaDٲ.J<SH3j$h]Cm_-bgI%`@lQSh[<8p•s)yua33h3Z[KfV fPEPs"ZOy>w^~.m@"$շZ1 ZCZb! 2kE*ݮxmR@hHFȶdT @֌V*./P.~C1P.J kʜxGx%lqٻV6O[IזK,&ml/m-t  [3ew/b0o&`c5^Mzn|!ӎwl^V/CҚs.l6׻g-{|79י\uLߖ9/SGU{MMjom{6OwPk1imm6Mg c}{ʾeuydYgwv='M6.t؍kgXTD578.dx|Uմ+va5Z\S{"%en\_ F~IQ+YM"Φ55Zc[egl,s[|.z---+|~5sy>b;f3.Mh8Sp<n:ZFG 0*'t>>Gc/^3UKQ^dSzM^a\F;mnjwz|./&=ZE?~/z3}?W}}/?ϯ?[}l{zw?GPpL#!18AB>u z4{ XCpc̢r{SN3Z~cr;^Vr=yǹcg&vo OIY`k͚\ZpL&Rf[n>]{YmwLkbkxH}׾ϫR;tⲏ]Nz;gWmulT,Vohs='p>Om\ʮ}orOlwr^wyGW/̵`kflץG\ޗΓ7Zy/ύ7}۫;Owqx>$9Fi9O$ț񾼵Uj9vu~;79nz}}Eֵ2[X[Me>/_=,>iO oLX%Lf"yOѩb C<8f?k:^?L䓞c:I_ UW^'?ǟ!0EBL1հM䵛_ϏX2`G<-O$OHWn^kRL?Y|2= ~BŦ63Y0cǘ&HJyREC~+% )Yoس<MlJIʥܺPHji#"87ѥ>oա%!hDN}ZLA LeDme1Y/{R܊!^2 A ڠMQ2!U,j@5~RJ-5]HANIVӸ M:L I(U?'ilK+:ʪ1G HpkKNwԖJpC:l 1B)Vq}w_ Wjk$͸ͤ-9fϓy _]ZqBMQ'$X ELK1d?uW #48tl:u\AdR[ m~PnU*É/i\O͂}g6IKiοKՏ>f^Nw{Xn\;- }cmd Cq<Ҭb?ԺRoSv~ǡXB#&5{ÿ7J}^u?oJM{#5N8$8!ͬehb$`hȦHh \R\W0ܗYg+%_"gq"Jw ?p)Q]%dSk8W/aZaLO( 6toԶfvr^\Q-B&DTKm>?;2fL6'B~ǟޠpƟV`\}CjvGeH΁NG*9geD!r &? 02~n#pZ D^#hgI/eE[}3|3×Mq\d%KWDثgݳl9ˆC(;k;8mׁRN-vm__{{3{npB!"! <( LEuSiKtfef"\.m(܃I\'z ͔hxDUPUJ "zj%tH4818h$zP/KP Nlv;:H'vc};G=?DBff3%c4ғ),JLe,eЌȎJh & dЪ( fg+,@+ -JHrlʬLe", RlƮ 1', 3#2T iH.N.3H,jJR&iD&PIR+.dTf(EDj:t9c$謨G  @W0/ HAf&Si;>-oX K!DO3aU 2eb1:0NA&w]# `{`&ʹGeƗXc9;y^b4ɼI.hlW̨UxqЁ Xܒ,!54P!Ly C@qaNǯn^#!E 3 Y~&Xu4p# bxv!nKXkeU~Wܮ>A2szDש~NiaY鐒cp+C/(>p6RKʆ1iv(2b!FKЧV1[Flct 0Cs4g44I GD39cY5&>yח!5ИP9>ݴ1h3M+Zu}#%-Qgqq=uӿ:=4'M4V)Jb^ֵk[}q{cc3A'߿ϻ!,ȇ~G놞On64pdg!:Gd"ʢOzϲ"K=:HHU߶ OM;rWmdf I?QX~Fѿ9k#>Q!鴥e2%zfruW< "(UG G)Nǎ|EJZ:arbn;';uȘ&UT$JA-">Z8YA3ή@- hk=+ԥWWZmT:q15H' 8ߊF F֥|>V>>~(u!F@y|MWƞ/o{߲ݳg~WgGwwGdx%%ɇJ2#LY)r DNL䕌Ye9̹TM&o|~s˛wBѼj/K)|M~a%3y@Xkn'9,Hnm+Y"[ݍ:U#KΊUw)luD% IUVa yH $>>fƔE=]plj$RHSPVs~oNAA]oSNǣ^O b};;O:RE!=lSY{ߟ=^[-w2RN{SWNw5X(O~V%/4&ZyB3hXmh /Of`K2ַ=}hy|u'HOLjUݵ}0R6& SʍVTv_5[QAW&;L̈%=$sVTED%U dyʻ^U!]#]I5;cܺ&m5Fbe7j]Z5\ne]QEXG9zTsej>+֧x}Ws)S/-6IGx^oF q\2+UD" D@Wkߋ>m9:|4;E5l %W4渮(F$gsĊ <ѷqjPHعr!#BTV|f *W2KwR`S,6fY FOd\\ACe5!rprb]dx 꺡q DTFVVUUjP[mQamZ*#kYLeYkJ\"wʹUELdEVTTnq0[EF"M[jjKJTVkv%*cZUX4mED")Ħ-*kSDq9mb*[ qDo^)5QVEiEQj\bэIV &R B֭- *[Ql3qJPJ[DȣZ5RVڤmEj,-,[YKjR¥E5Fְ]YO=uJY+kܘ[E*GXOІ!pMw 2xB|0e=ςN\)uEFV+Q[Klv)ݫ<f*Db,9CB**E[GU-F+ZMi gi9sInCAXy=cH1ŊU+vnUs7W'exԅ1U^ #6߳ZGt-ŋ5&b53Ą=vUTEbTzr>Fͻ30|欭9mʹfyvv p7! _UE+-JUeg Ôa<>%Mx̑TUkmTS":l =]7mWzWln*(툰QTQT Te¯^5tq#BB/76Ǘ˰g^l )y_t@$e gS 87. j䜞cM u3MhUdٲ$THN&oJػV~.v=6tNufc{A\mxa)"X L*ۜl}h6wM|G8WG#X9rK:aB/Lx;hNõz ;1TD|ZDFd6N#ٴjnpޡwCrYi}:7Se#$$8rQ7EUD%@U(CܔUD!i2EVz G(KM ;L8bQ&z=5ʂOO~M`?{3+3N&Iw)  B՞jlpCaTGD„0 ݃w `/C@VkZ·L4EOMI+U%QIUU+c["""3`.q`?N>UUXSPDvlVUUF[JzHBL:rY9C٠{'/ɫ*ƖTłXőEc 6TQ`3xGP_b{پ(Heؿ<=td,H*B AA``brꒆ*I`(f)S>@4[9mJ34ĤXJ_HkLQ4xGq6!5ͬES304nP;#V7;~jyGbD+4/~x{<$.J$/!mtCثuBcl7Pbne!CzC3gײEVdthwU *= I=%^UɩhQٮoQ&i\X+I 7;qg7gČH?P][v:.;v֨.EN`ќޜ͘0RDj9hM"jxI3Rp$}Xf!@gZ=-űq9ۋ%:{SݞY> m?OqÐ ;,{)c==s3sybœA䒰w|mJ024IG' zԁyuPkCB$nؒv Fj9PbszpݤyE/#rf=|= 5л8p3j %;75Z7aԕw?R%>PIO6}V&"!_/ }-q=$+pPԽytᯢHDquQЙ敘bI iܫL:@32ccÓ|@^mx.aOfdџL>䟣mpۯ miR.d؝_>To?g2F2T~c^;z.2sdY+jor/>qߐ#u PBlD4(5r|>gOlKKcMGgO0/~o@2=ӑ>Z1|0>spR/uz;6%{Q%!5d@}  "fr#ŝ0$'c!fJ|"aiUx7R ~.'N,Xt>~2_>Hd^dmd'=YvpG!Փ..+4s0]kol1 ~{|1\~'wp;VK[Q&oEzRl b"LzkA驺pÝ'wxmÖl%y5+fq9ǤotemkZeˉkXW3śC@IW Fb5hvQ0ړF߀r$drFX,e߇;L8 Oe܇\l诎`VPlq1@0pLCVW ڙؒJD8Fa3t ́GR,m}Rt)Š~挏lؠZaZR@Bw178Yea%y y3K^_ 3E7W)^w|X봚v& įJ{Pc®9N:{|냖62F:9! J sls㫾:l]ǃ (hY 3 1SA07ٹMj9P NblCx! .]_DZıWPwOhE cX,u.84S,  Ԉp9w3 3r $&(5-զ=t" IZq\/B8ӥj{d8jNϋX>$ @aqB>/M-'-F%>g5vT? b\v'u'Z_fkJL2 osҮ_`ht 9[5eXiOv}!D^|7Uw /nCdžxI\j_ZTN wKpxZF?FNyҬߥ2f:`c~x}Alo~i&}|2ͪeM"^=reNCB'-+ ?|^&. c[atߚRO urFױ8%jpa m74-y U.ҊSu{|ږևsЭ>V1&`#!-#D2oEx OX&oV/(zsi86b2:u[g+eriC#y}ٽYߺS#1vԡq`+<@om,\zK\o6.Pwg$}6@=!rO=&Ţ nxnKbӥ)X` aAN=Al>NϭO!jI+QlV սnNmb=zwŹi/7d7>邞!guz PAחe;u߱nc;>kcܠz4A棥$CtNoCNZR~vmb3̶sKq]f(*~Qqw+rWBL)7zώ:i_:̼w9^;Afeϔ2Oo0ܟJ.ϙHP[3^@O b6[aؓ,n5Rrq f؊]X5@L*ϝQ+}mi锖[#rCnSs(οv6q8JP~z(6=%R 8MϤlBއ?_4ATֹ5Vg^UgU&Ku-Z~ALƽGn[ Ε2c Qk'cWo~kzVh5͙bJ. fZybY¾efW/BYoJMw<'0@"I$Ie_镪s7KV"꾊Dg_` +[֞|W&:TUy}p3a, qVQYCSMXρ|}0z#B60bȾWv dAsG+6 {D d(ʞH|u ı [ 5]k+EYiwri*8`^V|~-:Q%) `X&2 %([!*-g:ƽc+=7pb&@xCS5Ir423'rk2ǫs{y-DPQH{GAycJBU "8g8.⩼tHLl7)W-b-M!.'ejۚE028$#O[1>?8;׃'S<,6E :Wq~$v٥ g]wh.as NOڅJo}ׁςe@!IpETBXw^RI߹G) 'H8k}甝yEfMs爡+ Dwؑ3tk8w]:-;`" x=/Ao=ΒrWpsbm;c7..8x8,AHWZ[${pƯ=&Q}rs!ϡզxw2Ռс`6k!ƸN5A۲t5ܹ_t8C}+PI80C@E&Gcᆭ9ix1i#y#\Gthd],UDё˹&4d!Jp}gcX_H{lɭp0 T@_9%҉0 #6b8մ(u]IQMR B!I=i+,Ta2K*Qż̎2!Ό>_K7oGҾa̤V|""ճDƑ =EI#F B =Cc)h>jpa?nmz U]t Շu)1I[o9囫vfܲ rҗ2_5{|.tb0?jb<.!,=g({ f(zOHw^$-ODwCg{B| /ce3+0]1u!J6|gz-k'ʰWjP٢(UU}ԟBY\X>p8Tᒞ=h׊ r, D–o/ U^{g(t_oM1AyL~]$[?NDT5.@Im$G͘:M?wx y_ct_pQceD(.s2󐦝-/3~΍ϴ"X SG6S,~Jݞs" [V);hR⨝jl,d'ș `8A ew*M*@! TȵӖ-;!&mr`Tgi[OY ru6?!hA"=G>&c= @OcL8`Iqf^H"\:lx۳MEϪa:gw=AUp|w=we螯zJuP@OzE@hyxwC4fg!yɂ]7cj9rq yI--<Y6&z|YtV={LB/G"uA]}Bf[D++GŵNtO3O~Q`%w .0>.ϿW S# MR2tV=n'f}Yݽsg <ҾG.Lf kN; uF/2e ׂne;Vz϶88A~㮠,\W}rn!K@ONq!x 3/j:U&ςLJ X&I~lDaC~PmOJ5D i| l;5]kkhpkx>v7Ak}rKQ.*X8s LH=U@/jd4dа|TT ]x^٪4 vb KppdXXrlWC7?TE=̚*3󀋑{eѡ}{ZaYME& ~(V;MϨ!Q:zfn@bvpBuA+a3WΦlfޠVc\- jaW9('X7PR'%o;sJfn7cǂ2Wkgi' q@&Dĕ@|F -`JW`^jub,z-KF%dǀUΡ?q*zӔx$K09wԎBW!w0h2Y@tn~>7f\7>iS'~IF ٠i 񅈏q`7nQ,eew(:ZѯHgݭ.N[ 5^^@2T; q=J՟QC@;sg5>ێyGCRO0{Aa2`}m/5`k;e,Tw0#(O6oE/Ə[$pJ`5*E\Ioh쾼O lӻ;z,Ax' &HxYLhf: |1֚=wg}ZfC엘Jck|aii#A]M;OK"(@SO뎠 MƸ{ܠ-9[qym^ !Pޮ3H(0y‚U[yi} y8CX*脐ꚃM,ׯMNwH} +Y-kꎶ^G9s:@5g>q|><[ NJq~nϧJ2&hVt2Z4"%Ƙ_y=RK%>t4י"R4"M ǿ., nAn3)o5\UK.#k9\$Þ*E!!{s,gr{tWNm|*EO\s;HdS%.ta(s}R\6%tI$j n9۠ nBQz:~a_ifZqY˔2uKbݽhm}/t- Gp`;ˈ[})-惃e3:[S#ؗ4JbpJ;q:vݾeWΞE|r [vIjV#lA΃dWT&(|qf ĥȃWwa=lrܥ:TCz@5& i2Ic@|7%P-wz&k fV=|\W |{Wsp9d(A ?z^svӪd +J^!皾I{c<'8:O|XH_%-Tyd |i6s ];_1f58ij{N- P3ONalkgu+, hR 2e1WT&DWDS?'r2Qb Ժ@ } po[G9f_\>|~aroaOJ]sU:x6o{AL4Hl`NÞz`_v4xh`#~} ST 3Z#}t 󠖛֮pMԼ(!a.}%ܶt <&Odi\Q2"iӬu ~EFrۀ1nzg}XFs8]FDžW$aW M@Y gd0a>s2.XmpHP}q5 .lS;#;V:& F=BIL: !^݄!qWk]@YBqљpD JA!6Am:E7oEjwɉ_ }7}P3۟ ;|_;oi9*Q=WiG{^2T. Ȥ3wɆ"!>x+g#~) [WTtx wkMyך !Hr WX en Z^D< 0Vom^a%&͒a'<0e6y"[c5"bk%{fp^*:<ے*B;Zud'aJRڞ ДuNd jZeq`D,{b]Jvc1m5m pA`]8 Hws!$Z&`%fqYD'AtQTv\NdyвLJ xRS84RS^Z ӭToH},{)O/|{NQDy8i"I qzu[ixfT^N(u3|N_8l~*;PkC}!@l$v$.#k-=ЧUi 0@R!ynDb$$ԲΓMtc^**>xI>˲ ,.jrlqD{!ϸt]"{ܸo8ey!.$L3Y+8[;!- _x:>%weZirw4 Ќy+.^0WpI p׷ty:7>Lm`Vҹcp )Oa3DV_@Q99\f+~վW8]yGxFٴA,ۛf! @ \u[I76AwKFz%[#K2LkK녬`BrGx{9ٰq|ꁊ*UbyhTTtϮ=姻<*:qI-tr{h=,s8 1#)$۽]T{p2ûo{G8OWoݕ6EAîRWQ]Csa#_<[5\p[V;M]mL%8-]l ah¨z 鬳%#ZFQG#Eb<5E{2QȈ aZ,26kYf M O~? UN~)SzMƇ7XCl}qF)ۋǁϿV۱ 3S)F(Z'.E?;be*eWx^kުv*VԸM I "R8ihP"q',/2aod8#*#5A-vt2btBe x8,ĻI /cw2؁HM ݪüAH.La=݀I3)y{6w@^LJENJ.E17`hD %6Gt8 /[v V*zmH;bH'wiD CZ}v⽤d|NӾQmsu[/w24gAO.~q% y[AVb9F; [YHqvA j-.ե!5Gu˺BDtv4m&H#cq(ZAh#8tn|QiC?9`w}_A$:K7U5v/!{12XzfZ:/hB]0NC30UIYoHLGEvCV790W1sOpU)evw_utf&kt/.|Phec7ku+vpU]8ΨR:pSINZ4J!&v}~ ☭\uVUpj:U,.ٳP݇6Xoq?EA=0 BbÌ>{7))0aD@ `F-dhrT~ ):%,P6ghVeo*b AJ sgEf\ wƪt;ۊI@M .e57ffy1C`Đֻ1F|2+]A7')Z9Tl[X1=R\ޛVe1@xb 2뷨 %K" 'h"ʼ`Y |ԙt [.HUD%1e0dM (#3(@$Λ9( ^ (lB+c{\S%TU$I&#L*f1#@E}M%!:TX$>4-7ӻ`xD"93tN Ql\~+%ͤYj +L3ͣbxZ0Ң'RR@b$M6lJ'7e]uERLxSX";]HjM160&XkϞlYPr{1F+`Gl] OEckzJ2{QF, j(a0 ! J&9 ^Xn7')Ű7EĠ4 ꂴI@ Q)JQ ,z>@l )\Wn芩a&yxNЛf䱕KPV 6Kh?}a,.nԍO4zI&r@33ۑ~0F$qA;1rkcKC3c 0Ml[B}]MB nZT5?8Fn&" /, PTBl&{ !y`DEI(4*E(La@+A1RU <)j0FմVD嵳/B/^C˄4~FgGuDbC"9E,΄"3lәvjڻ]\]Vֵ̠Tߝ5P`H+63}!eccFtt1 üBZin݆L!ȚB=iӴ6gn69Biit~u~߼hz%\f@t Ɲ_tBz ыx?BgaREKOmpۍ@諹ne[! =b8*BUḊ1R/?'+hzq8/ۀp H %ff {w =vC @i0yN$doG[v\)}]}~7LuSM4r!p.[#]du'GbfK6ξ+G<K>Hy(8sG7CJ i) 8$#bco,(ɽ>MO6-^B ϗsB[ຉSv|Vzn_+otuNqƘ8Z XoljYd_1cWƄqj;$|0N҉)sA:"\ nh`M8 rder q+ihB.e/|%$ !>Z^26QpՑ}+w']_u:Lg %``:?:|RdNʏR˯>𕓤ۋml"Zr9= %p N2i">BI_4hiY60D 2=x 6q3[t|^k}yv7FyZD +4$P9D66$4m$ZD$-To!2b!hIg&%M罡C IP@G`ay$h{ʇ:G* &w[1J1})x(o~7kߏ GdOv3eRWP]2]^t6\X( Rl]ʹݨ|' 3{rm+={fĩݡQlF},7 ;a 9VLew!o{Fw֔*3ֱ^>9̺&m|rr;T۟Ϭ@!m@@5\`18 9@XQ(Zi%lUQm'uJ>:ױx@EOy44QFesOk;$rHaLgYO5O^|2 ؾ c0ygP `klna<Ϟ-pA7 I;7qLtz[D#dI$CW] @xtaB"l&@-S~Ïە`b mY&iMyxSÜ Sl + RSp78 ;k( m *P# =#Oq9 mSԺYlyBdsH%[9kLd[Ƌ spY ߆4qדx!xB2XZFΖ-4.\%' d3,E[Ť3 lrESO9]zUWKZc4 PWzeQ˿@!#KT-`B(a.ru0|^)38s͸؜A! QKx啖t8|I>k B}kk<=3;6vM;~Fc+AW8OZ_2Ǟݗ x3B6xGzD[}?ky.sBkTb<Za:S k*ܖU~cH%}B{f~fJHQ4U:w+q͘[݂_:ք]KE,@|{DԎW㫯vܾaw_$B,?~twNl%̀_{}b׻<& oǴAh(&~=kxEx`POiS֛ʌ;ヲlc#4$jcܮ=a#=e;-Z X|j쀛ivѥmPcO"9gstKflj^8HEfn1=/neyuZa1rZ*-)j2m!I2}'W1fH[[Y ( p>XYlio r rq-^j6}Җp1^<5&5 m3Pm%Z-Y<UH&]?=g'Aw?ܮAw+=Dljw,0fݺOct R@%bgti\:5-;박Qvq Aǟ_k/uӶ~`Vuh!!`)&Hdz<\;=0q[>'CQ`6V6UU})u&}p)da"Z‹| , _<(,Fu9ywM'ʊȰPN!.q?b'lXZ`}?"_4f;O1)[Wv^2DHk/q2uN4t;DbRwD HJJ (T@DEsU}rֈ,">Ӭ.F+,hm}[M5C.y鑲k'Y-t\rWx sl:7͗0|e9qL@?q ;q}Jm !m s~bXVF44anOW^JBuq \)'wUd}wFY8*_ ;0Y%9n RU#xN 4C0!Ղbukޜpܢ[kkY=l{̷{{Ql C &$oʲ \&˱28A!w79XXS˘ 7{Y ZVZk j %F#9L 'J#3ごV*Pf?1H8=hq*qv簃B|jE8|SC0rȒ`Vd&TpeP{J%\<`SyiA#jl2)=_S~p}#fuNΗ0{ށJ -ʪx/($)Gـ wdIӶϭնȐl^|dq釨{pﬖBOG;}e4+.Laq'3CR|S r'$a5q8 `6. h[FlXˡ D=1@h9\VL$/'ATpBaMGń:2+Ӊ%sˣľ;nH@)S'cɏ4fR<(|6w/"R)bcN$! )*lQ0 vׂE\v+存B&N[Cӻ ^w'Ƴ}xd <5>5[jfɧaF|:o4ɡ- |r)űE _֑' `2ǯwkM5oEt̻/}4Mmk*ֵC&%+7eX .2NAz,Ol} ;Z@A}D]3Y}[WQb7mϓ돴@B}8/Xr1~=<q핼P05W'aYa7A~n7U"}KC?ӞpX JЃa2dM>N}<8<ˀ`qFҩ_i]w^6fo:tLv:^P)j wPWX~ A#0Z\PɀOa^9uo]盐0̣B+jjkySGMo ȎYY+59ҵ),I)Az-ƽ?@k9x}@m5l?y q1Y*!5żY+7S񳙣$V)} yy:ꛀZ3qsT¾i:My"OB!<(soY;Ș?pdɯg!Ζ$AIi, lX3~\0NH$X@|O13AZ4 M 42h1$ Qd Dη J]x72d;`琖|`H-`, Ξ!`A=C m VC!`%qa26$ՆCFmVv(uIRKig}v"R yOg3UW$}*Do.,H }VyȬo+ ic2kT&c=deBAt1.|/2< GBw<~>ߍTq? $]P-GUہ|ΖkwGNe`AqEߌ.R!<3*\:( gըf6V wN8^ 'Rc\vr5?I>L}JNϤ{.Hj:}( /OqGQCZ5.ތZ:-k W잳r1^r҅߯]m7>,!U ۖű ,3> &?}]̂w5h]HKr6 6j+D@UGq ,<ߝ菚$A9A/ePш805bNND5E--Hݯp zO]7X;7էutտHC@]ق.[=9q62+C.,Q#~ 0}m'ktTH4|mRl+l8+9*cL4s \_r[;=٠4>@Sib)_=j]u`蟡v/:ݸ8.lR"&骑t1={<`?z9XRGëv`gv}x-OЯvD LO|4>}=3{g2O;lC֓H7b@iBUBFk#>368 >^jMH{RsgFo=| `hm"y2~3 ō^d\W#|W@?QQU5n{!cqlFi'>KeGnh4TnO@{pu,RAsA>ۯgd(KAB=UQhwb{̶yvf^LJJa;p婕E 3(J B{,|5Lې:E3(;[GțCRw,:Uf v!i~|C!pps\&VEd6Ǹn{~u>H֍gk_O ~Aɘ)ϛaĉ7A>[_C,|ސd3ދmZ@ܓ2E>l,#^]T gGZ@:\q P||?;2zyPdH*j/a&3GxJj`>C߬>>V+>mko$<}_/*+czs\ #>oͰs> Ỳ=amxp2΍ر`ER>=rܜp}ž Z[pi~Y-wy\z s1oJZ~||,ܪAkG %Nm_n2;O<Be-e̜t5@6 7bw~y{z/]CEs:J<{I52%3 33 5Td\X+7ңPmy#ڶ& p&8ƃh)ZW*`p8{~,8:^N` 8Dc UM7$2/=<oLTR,Q=ѹ@D8ϭVv0P[`. Sdu1HR#kϡ ޺}xi:DZ#m'H$ Y۠n]=R<ÂXHm!a;-a*%6TLaZr fh%*T {+̵m3rV/m.L~B!52$.ѵ*,,ڊ~Qa`tTBHbIi^ ߒ F̅I$I4`V%FTi_'%; |a^Y Q,2H~#$nQjLfPģ_YA~ڐRQ*)J˨ԤI&l4PMXO# Ryf4O(DUj~T6 q#*X"&@kW*V9'vN" ѱ3|λ!;Q><h@ЇjMA 5hPBUFhus*4?mC;w#R6i'Ow.ꝣIf7\a{z['n2Tw4sG#,x.ߟ{iEFe֚ʯV<1~h(Ӗ9lRu ,-tv4ft' q,"{l3& V a'4R| Bck)b3K@w^V &͆}ioܞ"Aaμ,^7#`]6$ /_ފ|=&Ӑ6,zOļ;AQ,t=/ADJ<"";Wa]Z }'f'|"ϥ#DY>o Di)ub:8Y^.oɤSd8cW;57/̡~Lx()\f5S-̈́5=2x=H@/ .g:C %~_w<7=^S|;/x e8:RqS̊9.roAY#0gXp)z\$V%r;ЈbZT u,-pa鶻e6> ) iЧ?,3 ?M˼PM:?9Ԉ%sX G4dNA u.=Z=ˬw$#KB]l!QJ-z`X-UK#ek/B]mݟ r` :%baҤ/D>-Bw#Cn{6 !(XHH0RBe(>ǭSNn/ I检ӉfQ6);HI0@P>'@R\[c6=Y-VCc.#ti&t׈zHT0C :2wLOuUOX`!ڃd#!0WJˏS?^O ' C9l!')6n\6͢A~"yn.h1+z65oH;s/~RY*͘o|GҧBT }@BWY賻E:/mvvD kg;ߜO>BH9yˢh99ĭMJ}M?!]]l;y86ƀ W b[[p㌌|cއC! M`,!,,t@dz0Cor>2HšjJ19WiJ<|/U^on>oVxהZiΓn\2S!3^LB$ )cɠ~+zZZLb 6$>U]3Yb=iEa]soNkՖrSyH۰MTkwJ#$1@d"?1}(q:Nσ>1K<*e]-JVY/IH,s߭հkJc/1+;@0毖n9,[Mr8ћG)94uҦ,[)\O@P&uvs;+=E IeA H5}cZsy莨%5*>~RL:^M[YIe}ȉh }y"`),f.>xDqOhzяFK_0PIOsE ڋ9' . C5C!FwO#u)|zzn\w#tܣ!ڬ%AJ[)j/m6Nze!dnn`i6O2ˉ<0`bkIB!,9q%DDr4LGL.bf.N@#6Y}!~Zc5ep5<Y~q-3"IX;( %v YuൟK7t;7[S ` cn5eiO?* DYKJjCCsղev|O~#v À\y5o36\: 0=YZ,ِO?縄E ? /.?[3+>j^@PplwD|n:HT0="a\ℲGTLWh੨wu_\ʈ^Β'/P8k!`AӽUfݠ*eqM  dl־!˝Ptt 1enރvGΫq˺-'G$=?V#|<CGCFmtJ<3Vp~k-L5J#2QA' C"wm5fc-X6ٱw>TQw!$f.DI^0CLZVO`4%e]CW'(nmuW/`ەs ;Է'BT @ _&xե6D'Z^TWÔ7繄!2qQKƐDm-C]LNh;1ըE>m'՞STi,\dt>^3P@"m>im!}<$Q9j) eGGn[mlLGfzjGF--$r[}]#x@ uC CƞZݯv%i[Z˧Ob$Я;pJ$?ASBZB*!S=ye~6nR7 %靮FH[9O[6 ʻFkΟ}0s5Aj':jSY%.Fh- D1,pZ$md4Fu]:f+aSϓoo{ Gͣ)Tʧ ~2wj&Uz9adUPaXb-KEȰQ%Tk@[[`Ոb( dTX-h[VEDdAR(`",5jV*#m* EP)mYZUj+@3(JF,A),Y, $DDFZUbFTPd$TKeAb0V0VX9TTcF-*Xȑ@Y2VѴa!UR@X X4Y DKR@4@=P0Jeip9H2n3(,V+/c쵃R: 7$Y|:ֻAf=ajxt ¤zz"h-.f0I%)E vmkG'}!'K[w K$[B$ |L J(jR'K?6≮EH\, n|B\P05 E$"xD3S}gF..P7k u1Ja<4ĵ*u/M-ˠئ5u@ K}W}0W6Cji=߳ yGoSiuپl"SʙQ9WnG Ėa &6WLU*S80?y39%,T(.[&COt?a(",+@4$ 'iM$}`PG>R ML3 =PN6BRyŧOcbq40!8ZmqCQr8s%T( 5_/by s;Lj u1J^]^䈱}TmGJVqL+ PvoA0lĴ,X7pl/ۆ<8[r.۞'Y) upx֢ VLD,fɖTmC2ɫ9i {4ԞI`Uiqτ2v_^s4Du^ )JdF*H@255pO.Sϲyq((&oU͏`q։9!n}ܵ<y[V>;6 Hђ5N@p^SKaO2DD(B@~g0!/E`! 0eGO_=FtQ%T)tRyi!~H"/ bD09~JTP79} 2f/S0cuVzP>ow4RDkޢB4!TSШJ))pbUzy'-a!lmWbC ޫ7tYvأ^(T4 A"#hPҔ\(_Ꮛ;vDC@U+A !u"t pH@ Pča*(ec .P|u}E!AV9ϧ"m $wbdB1a%mۑTTؒ0zbQ"[#Lߌei Dٙ鑽vK^騜OfC VtҲN M _);Xt$]T5xa97;PnƧ`k#!IPZ% 6T\]m.oM1.X?9X|%<yB J@C @d9qH;R6`W dNv9KMS;`XJ=-žNӳ=@fyRe#pe!R#Y&9Y@0ZbU܌ 9 d ȨPFB8DYw~ӏ e ä8Dc cdny`@EGDA<Dgta9 숤BdUIԠp<CK R1^)Ys=M"ӅTb9f:O|5Jb|63 Aȶm=k3N <3`{ C7\|`H)o3cXS!21'`r]i`A )ywѯI!FB1$0f FLPԡq{hdL,Zd gVڼkV{QR-㦚ջ^ʀ\yZ^_?fy/Z*ؤHHPbA"0 LLtύeHI]Z1b+ ulUT*<㧻yppWES-{rP`ȗT|z^~;yVA*屇_nxH5U&\`2+\^lcx*Y( sV U  ʢ0V(D~Ix֗fѮ Zit&(-3i649g$f) P"M E%*ڬ 'sg@hbD$Nʑn*' "9^DY+yuP]t @=XN+oQg0.Sf|Cv\XdVKfs87"ŔYyHM0.M^sco+cإ*+F` %)C_꧛o2`% .exҦ9L'p!t!nAeKۻ2ٴXub,#%\I78xz 1E JSq&m f;Ӂl D7[#V%XmC)4 n)fsѼsUG$L:Uˠi܆% ypiQ0t Yrӟ A"jZƂ@شUI~=dzD/0X5(&'J !4v2I @a(u6|p5>y@ <=ܔ{GI*ๅvߗKXgPy@ ch!(@>Yn1߹ 3Nnm rZu;?O>k|ЪPWizPx'`0d=܇uUg)IpGe^34rdis\LKĪ`-@t4}Oo}q^'sѶM׶fiʚ>}(+i_R"" vX]Ch" 0L腙\t7КFk rnqO=Pp$8@*-D5>d|ߞ >f}cs^='zof_>ߗ$>qW}{}׾HvgpȩO. ӼvQMZv8kJ٩[zM7v @!d};2՚ R3s[Wr5ܹSAgAκڶujwYLQZNRYv$sŪeYVq7 h]-'kkeg"1q5ĵw;@Q /.uPPU>P5<si-,]+ʵ)EQEf2 ks1.p!V^c055d6ff8d6fde12d02a80003b8216d348fbd541ca0f466e321568b16880ed0e-other.sqlite.bz2000066400000000000000000000132631372027637700360670ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_03/repodataBZh51AY&SYr=tz`Cӳ %%?#*{M O̩SxOڦOQCFhidzSbMj=4jz6ޠhy 123I3Dl) 3H`L4 &&2`h`i` `0L L5M J4=@4hSHɤS{TFISAhښx@@h@ M 4 MhOP@44M1M10FO3) &4!i #^PJuL;36q˿w////1<8Zֽ ;yMW& 3 P[^-a!g`Ha&waB /r&D<2CDZ6M!A"$JY91\  Ko=Hg k`LAd &4bp 4,A   ?̝,!ySǏ'%'( +@y+U$`AEŋ(_\gmb9B#"QF&8yyY2$ v&7Bb00ц7I_}kiѥjLDhJ5y4zgnP?{c$nouChX] /䙆S/. 10\QW;*7$7g GKW$MR~`Q+uLaK:j]1eժG?ev.D$!}}_0ntQGS?q}}wV@VϢS3'?S7d Saʪo¡Ӌ.[w,}&+-g'''/!=<@{~߬M5N8<^M#8QAw^w^P ΋1uyKs魨 R  cׯ}s9s[͔ׯ^zה5R鶿R=}v۷nݱXBk*0**N ~)/2v8cEM8z|ypl1ԥieC#sFc,e‰ R` 0 3!BΦI! DI!!$J 2LLLL|CHm! ??7vZ\?M!wf}loXҾ.K2_u?Q!AF(F1YЄYiH}w-"Ok/g 4_x:EÜ.߄mE qhm<C0 o< nG|@7 R kCVG:Ȃ9@Y} ޫ˿tc_Ї!tjAރBڦ x@6xl.< CtaFK9#=!"HQ) !$qI ntk}O>we"єTV0EbؠR+ HU=gӗ,GkB$7b5HJ6QE֩.0RE&hXI"(&""+3%JñvRf7dHBQݏS`l]rAT BA9`B ]J$vJB3\V-jf,Z5,9]nS37/d%ZkbXŬUdν%k|EE4l!HC{ڰBDbosX$`PuEmnV(0kfܴZW/4V/\,\,vXeYZDE[j,0^[ҵUXJ}}}W 9Sr&B$P7(kҤ)#k &hZQ52%Ŗ[%- KI)%1Xmps/${[vu@l\ң*7hqK\xlRб-L *ss B9ċQŚx8&9r*ŗ %,[8c\z44UdUhdFݸIs5V-SzVn0dܱC%X%s#T`ձǍ - tͥrYbf5lQzijVɎT%\ϑb XJdU,K%W4biEW%ƶ[o [ܕ/U,mUj/dڕ2rj] *N,pQ})rŌXVI^m 6Kfʶjfի& 0ͱ, 3ka-*%{6m,T]X+pS )qccrm b)X{KJYн2h%F3;72`TUF &i^|<:ؘCTn4dZF- ܍,ŃFmK,^id͡l4*jƥ0kQC:+)FjZбKY40kZx`J@V$!!da.AMzuպ-f=NBoikdϜFtG}oH$f'!Pf"DИI$I$I$I$I$I..njEu#>w<>o8k[tuh{ca<+|Gգd=`鉘Dtzճ͟:-7Lɶ Ҿy.#0I, E1mNI +W%їYPa}\%<kl-]QNc` ~GUQ exS41=o+nwl.H|hëճض_&Cx7K㶶;Q|Վb,2dVw,X7&;^n큧(W14md>e-mM̜Öpdb嫆2`8B5A|TB y*$j3`D=0|f^0=F׃k+}nxou42u,d5Xwx%[2^5VɓIMXFkZXbLPz䴨M kXFk] &b`LDFHrDrrlXʽ+W,Zȵ%Vȱ)qX[Jjib%+5l-^ųB VťF [ JM Z: OuTM#! y1IwƔ ["-"⪬9tts慊\ŋii\gkJvEXfB!Be2Xt7.{˗ûyܼrFmxbb099".uOSxbaC;'-[Tyal!hK[`vl:ݷKK; SNj*ņWYYƴkMG3gs x9K˓ɯ.thmmuo\!!(X<)mKou2v>-D<9sAeB:`[,Bc bKEQfDA?DZ*D x:Η\; t;Ṇ+S%*f BX*HkfԣK%,u3ib͋Jn*fk֯PYZE+Y-f!토޽ C Ca=}ZxJD<"F lWؔ5uV#uhȤ{xi"PB e_XX |QPAMp=<BB , 3 n>0z.{ww $pnf9~n=@VD&;Cϔj}J8۳-G {,aI\&>* 4:rm#T葺O!헍\x50:x+@^Ut,j1^>rx( Z(G>-@D "^AYlA6 ,"*#V0r >$`B%^n"w!z;>lC\n:B5Tu[kkdIG 0Gϫ o'adQXR^b= Cu[5 [(R-q7ît()@@ QˡWUVM:K/cae>B |#w$S K-`cb8b33d29cfab5d51e91dfa4566d67f8dabff0335c863689363b005755083639-primary.xml.zck000066400000000000000000000365421372027637700360120ustar00rootroot00000000000000librepo-1.12.1/tests/test_data/repo_yum_03/repodataZCK1yˋ3ҜߤVmgڿ3\66;WU69j_XG@[v'Q[QCnG#@JqMrڍO2sF6$lvQht;kr~fC{ Q.6.uK_ I8b 9A ]R\тՓ= 40x26M1͜.QibI]SAEJJ"[=C#ա|QqVwxgm"`pZ@QAj> "?D{OBSw.fM}a0*)?[Htmk:<=U`x22ƲbB:aͩRT(/ m75˟cN`s"JbxatVJ52T74xIV@ U7Y*Vk28uI(/`j$ m51m8i4F$:mk```FEADGw͂VGXi"SՋk{M&JDIUU֤cX _U/n]xSQ)% јnQ&ߩ=RAKi v@_KnbO MYesk63ha=5\o<#g:+nKmp^oOSr0 pxs:Oy /Jh[o֟#3I;Pɉ 1Kq)F(bDGl ۵d 39=/Jj*)VI%6:H)TR쫅U*XQ!Qβ(P)  Zc\.Q=VEW{]-YIJH!<@nnH@d#FF^ o/xCgƩfFBsHc.x@V!gl6_mˋaHi ZkU@ȽQWЖ+w^KXO k;qBf%VA>u [ʧ-r犭CB 8)$D)u%2Z ^:h`եG4q-fYwzWgF&nn3̅1:`1[7DtCp,6X["j4|EJԮ:Ά0)AN237 Jht٠ Bhr/64F)A>hn[puD]h@`w(/` %&p<8_Q+B@{]Z94R,,H]. &6IG%q {0#($r$IAS"XbV3 $=R{lmF6~0zFz iiՃ y~ֈT4MH&tͬޤaX5Ǒ_g2%GaekO[HZ& y I*[j"@^wC$Dw׃M4PH:zď๜5.R9 e4.[Mvūt,)G?%_Sn~٭jq}_[Բ|sz8׬,cq9$1L_x<}f y=9eOw{8RѴ<"Xgbª7iζk\I$R_gV7Ejz~*}:]ٖh&H嶸m,83mԶGЕ\Z/qQ]R^<Q܃ŝ\:KuKF+ 4YX L#7CT6ͬy(( D{<3*MB((0d,KҗJ廋>͋]g:qJvj%vXtgrǨC )300@V8qP$a($?sk_wcnbaCِɽ"=^(]V˭1?(2'rp)u+úx 9$ 'YkEEFL tlq(jXjHg=E.??B: cy &`Ժ{7,mnX'_^P<qMq@+]tiVtַ׷)[x)6m#vԟ@D`_m)-DC *Oq]S6)Z+nAL}AY2<ʺ9`K 95C+;c`"*E ]J?bAq^YCjGn$o\ i1*([K (7Lx䁑2v5~r̦aY뭊ʂRQeB(/` %%q5ǕIrq+c§ T[D}KDTUrbY:9R ByrY$IieML!HCXt9HJӑtu@$TMcͽIzbuL m9@PhM{I&^HEe\LVt8LS^-@ߤ55*Wp8 + $(j(/}$mq$q"4ā87qyǤYAy#9o/3` k9g2 Xz2uL\m. CMwxeוX͎Lvvs3˲o8֭z1ѽrp,yarHj,(8<4 dI`@i7 e&3nDb=-7Ɛ&F 7y,˽7t|RIћd3g Ro!ү6DL4YUA*ŝ|Pii-]AS3 ~ҩAU0HqPK(sê_w޷s3L8%7!Dr14c,_Fzޞ"zkokβ3{6b*@H]rJf6yG0M8L%iOϴ;}+_X&;_ހƨC )300HFx4rA a  `"?cJu$:nb"/9jP'f8Lnr*6Ďb 8ڸ0*S(]ù&wΈ]PCg+e Ǩk -Dt @^Ը؎/?s5db=lD,8Q |y`/+-yZ䕀=/dfT#^NhN]H;vd.q'_~~Xs39ʧUOxQ9haZ{kXUjtzy˨ eHY zЋDcuj[t0WbB蠰2ұR{&Z hVD$KyRt/u 6hɦ=6E^ GulN zhl81`앺 Kk߇?=&Z.ˌF,1.ܪh;lS(/`]%<$r5&`\H?-_jV-ր]hp6qxrqGyvQ% 9T5HsMcɂi4Mu=R#]%/HJ@(:xS='Q{t Sg>I_H44={͓dZw>hL*!鶸VoXXZS9i>[kJlKڭf/8-"&*򛹎u>2x">4A$?7Nđ7˽DQAHΓ˹RPalsΪy7H!d,Iph# WCTRNҾ;SZs5;JI?l.(n5oi5Yn qZıbeq !yg° ߊ&a*fȉqҷm@4-(c=/ĵfskዞg$_\Yx*h DYe\3He3} Dpܱ#i?-rL=<U01%C0fϿ(Heع=^ϳ-Jv#"9DQfk Ѷ<Db]êk*o'B M$F,YfIlXw|_igt[%}kg]R}{\qZky+[ͨ!%CJ1̀08G ,Ł`#@26?฽~[k`'4r{ ϣAgt쮊L( &TZ{Č?3 3K[RRP 14;6g},>2/?΋aUs-t:@:Cv{uJE*KMG_t!euB*J$Q sB|)呄`GqSB.`N*< 蕈1;j>Y|E+Mݽ艭\ԷHrj%2_x6DjDd 2.?DZyЮTH<T8 [0WCB n0Ʒzr,!28g{D1Q~k0 UdX `؁CNN{a]N`Zvh{)ʺĊQg~*-kbTM\Zf\(/`V$m# m %a[NIDI巚Y(daf%gvZj #b  N.<ƞƛA?eQEaČhuTAܔUE[=3@Ĭ :TG.M/TU_5_t“'CyIs|2J@b`;c+Ctg6g_9O') ɷH>I`Ȁ ɥ1hG،〽4RvUm˪(EkH00h܏V qyNXA^!-4 gq-y-ЬWrOȇ 6F=U)WH$ l7F_E Qs;o/ʍERRj 4[8`1'[r~m4ײScsm/jS(IZIzѧ(>ulA4 X#(aar*HeAƲcO2]eҞZdӰq#~K~Dsϫš+0ڈn^lԆo3Nx+|] A;?S@pA0z_-L< ZQu(k@VWWqݤ: F&-(}FTvꏅq ?+ uՄOK vB;`լk[IŬ3Xє*(/`MWJt(66$[\ji7ͯ"TC̡UNEG7X[AgpnSZ1rX`p`A_@a9,VbJ5ǚuB {)X# LL< zBx@" HwxPQEd"a@FAvpb' ANd!e'Gp(JxW+Jʣ+">pP:5 Ĥ!Q@PDS (A!11 s@T1 ˅J灔"p%ǢQaPb%PYNo,14Q#n-@daP/M^AtEzBao]) {;6853ˊF}Q-goQoj#ncF)jF7)Tr2Lݷ锹[|?%s}Pý=wFGoo]3IeȘ@7H$EQΘ/wwwwn68pj@A 虙e1XcTUUQT%yyy4h\kv%q㴦i4445 ""*bTJ-yVJ1 F3I^ 2Lf\#c> CB X. 9{Z1\Bϖu rsI͜t,#?}Өg/WR0<}lMqfSg ^R%}+Q/Sc~m4saEfap-M}in=s.;BlR?#ܩBVtT1voAxSڭ1qRI?!S#Ĝ<%ef]+1vI}/I5'fRg5 " 0QƆecq8v ؎eFYqPFJY8p2Xn5i:8C*|$J42 ;CpM!H#gÝDT[8M%w-?k ,LY+YNfA `e?Wp3Qyԏ&ngf(U;Zxбj*z3dEEgwXx?\䃐=)2ZP܀`MUsYN7Gb<[.&}c5+H^VaX2,Բ$֩|p`!Q0h_u H14r \rh ֪OB|a֛~-Vjb%!uW8F>ȧ@W/sa'b|J&a2gϖpB@To)BۉBNnm,sKnqf.@-R)#4%) nL5`@#7`-땏 p ?s6LRD:ptv$lfC Ţpkk}:Fdž j35Ǧ06-R-mu=-IztmqA-=̬j 0cbtS@؁O@pFs5tҭ ̧{B5]nbr0gC=* \ħͅkDkC\k2HެqlIIcʘS\e=&$6}U,wOJ1wǹVZ] k@ {hHJ΂es?T"t r/4 8+Nc:9.l ^r>̔$^8L=:(?*o(w٠s .SirYzKjkUȲ xW9H9!7J2OT/ǗEZ dIGd~o :VeEKq!|[/7پ-7[GO$%K5(l 1QEo8? V9o/oo0~OFiSިuO;6^ ^!K֒HdYom62JM*r9 }>ל그ܧ|:"SssOYoKMml<,8z0y ﮣ֪A~AHZ xڑNX1p |n4"?Ur$͖t4@2m-e+AvYbf[6-oLy yM`wdTH)m"a(/` M %pi,C+>q N sQßyS-o-3ޜdAʁ$EZLi:͌EiiIV$Ir%EП%I \ /i _>/R3/ٟ,Ҽ?MA*FQe3KHi=iGjӶ&U?&5ܨv B\6k Ϊ Vj'Ng۪HѿɌfnJq_/w5$ <4 7w `Okk.5t]機Bj*<˳UgzyJ5|ބ2GSfa7Wm3H Źvߺ"QmΘGT!z}V;,DCě\#IwA1|G4gVa҃S-pB_ zL(/`S-H c(Dk4mP3NkrgttlTnd`<\kIc@j D4?os#Sgk@;I !3V7>9!A77 A2_QMkO[7wrWʲuTPAQ@3fG FOn}¨=JΝÀFŦ¨YܢdVJG]i{I+AˮUNūBp2%>FD|>c$2wBTV,āS( PZh0vS\nz!7wH @q8|qnNl*Xb}ǞNFE+sZ kb )'?*GJ㛌 p 8 6}_`lʵG-YD ͒.t%,aD@G%. sua%'7JPw(V ._h8ݟ3P|>!N=8~y^i,wBNh0 ?yZ@;ޙHwy:9>l{aZ1r(e 'N~Q+J_zmwͩ,tꟜ*VLJ^|nmxT#to`p(@v= H@STZz0gZ&P=ڋx[ﻸl Dn֐ohP%oQzMdR1%K:[w+0 *kïκYq[^]͇qI**'LvNV@EzS9h׌2*տ OxuM(@f8Da8ݽzd ;ǨAH@q JMip a A   bR !UԺ$DVE/L99֖C[g3LVΔuA$HQ1=ΒIz)ʞe@ zZPUxjVw.I]$NS2b*Yn5hxfȉkT09ԍrSX4;d bVtwP䨏9~9Z\K}pZ Tl/ ")y#(X\7.:8 :{?8+gn00]p?Cfҽ837#HbB'C lz @ $X*MˎC@y0GX 1CjbhQ>psg1#q¦eR[TyԃQ 7A󭯷V$zXo{2yJBx>5iC&Cy סϜUI ~uzQX=6=ĩ )|'&YZj{w7 [%ug ;R.TOp7z7mv$Zliby_6He[핷1PZF,4MFG,p_/J=݇J?U %XW{hp [3W]zl#+$w<$A5-4vGIuYͶX߉ȝc>!pB[F1N^Ia%t-lrL;,X*i4<&nG@!GoHu=j? &c4lAө_)id!brk|4R.,2_(.HC釗"a0wCFZ`cm 1w`>}?ﭠd^Q0O6D- -Hs0sB>Ls鯃\AWua^"FZy3f^~=[Xr..Մ}.٫,3>2];]{ٯl.qDq0@q8@, {@Cqu(-E/4 o1h'<`1c(쌫 i~|9ɧ"5xxdOE h*%Fi&}x 'N8w5UˀN<$Aq}4'R oCDb48pSZy̌eCYo_zaip 80,/WSө[Yi2,쪜 BQ%CH@Ơ'Hb @@n 0tc8wGb0Ah U =7O&=AӚUL^0YTDo9/•Necc\XjcT0B‚Jie xYe Fwa3"01sI5KJUc\ce ?<٨"i3.+ށXc::{0 Jk:eU$~GEIap--̞RF&I˫W %%B"lPs V1>>r;^( i&a)ۭ_ z \-N;w/ A;L"U;O fCAY01:9HD.6""I_ƶ|eSuGAs\=NJVZ$ I1X$RqwfYf\g3(KjZ[RoLET667&cm9z3e0,3n*z a 8$I@b)91'$[ʰAy&l)I_qWcEGD6":L7i P 谌حw靪",^6lL./<' U][!J?>m0򩨯D]/  p"IgRl' Wp,9Q~DŽNeonz~*;librepo-1.12.1/tests/test_data/repo_yum_03/repodata/repomd.xml000066400000000000000000000077001372027637700243340ustar00rootroot00000000000000 1525701694 1a1f36da53154f18f2275e0c5da238b7cc5dfa20e95385f4b37605ee097efbb5 a47c3009ef971fa66ae646e950dff6e110efad363cb8358eec076bec034ed978 cb8b33d29cfab5d51e91dfa4566d67f8dabff0335c863689363b005755083639 1525701693 1525701693 9541 78278 417 42805b047adf8e89e5850204cc595d01f88eb2192c7368c8c6307e5351e51c05 317c660defd00f64dcc9f6841b5d442d54edaa29ef35898141ab01b8588dea6a 565b029a0d218e58d7b695be6664b3240c6c7b2dbd23e29136eabb63aabc8c74 1525701693 1525701693 50907 339783 418 1fca35b586d42540578e0b042508fff7cb0a8e3cb7c83330e386c891f66935d9 931db6502acbc5254bb391a1737201466932b94bacd8e12cd4a78485b2eb1d8f 3f694f7c23d07f5b436de790791d5262406dfbe9b47380f708fd2b2e9bb8aabc 1525701693 1525701693 3078 27794 414 014663433bafc2d09e870a6156e6169a091939916ae4149e1a951159e56c46f1 6e67ea9b144adcdc8b8151bcf15d677c521ad80751bc93e131833602af5c8a12 1525701694 29310 208896 10 81166ac5f4e2b7cff67fa5da3b8e8d6796f705dea6fdd426519e118d6421896d 099bc83555817acf4a733d8f72a72e7dc02294f48135ba3244f52691af35a314 1525701694 48927 159744 10 c055d6ff8d6fde12d02a80003b8216d348fbd541ca0f466e321568b16880ed0e 5208f5ce2ea759df1d10684f9198853e84b098a6058b619a982f4e14d073b858 1525701694 5811 45056 10 librepo-1.12.1/tests/test_downloader.c000066400000000000000000000254151372027637700177600ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "librepo/librepo.h" #include "librepo/rcodes.h" #include "librepo/util.h" #include "librepo/downloader.h" #include "librepo/handle_internal.h" #include "fixtures.h" #include "testsys.h" #include "test_url_substitution.h" START_TEST(test_downloader_no_list) { int ret; GError *err = NULL; ret = lr_download(NULL, FALSE, &err); fail_if(!ret); fail_if(err); } END_TEST START_TEST(test_downloader_single_file) { int ret; LrHandle *handle; GSList *list = NULL; GError *err = NULL; int fd1; char *tmpfn1; LrDownloadTarget *t1; GError *tmp_err = NULL; // Prepare handle handle = lr_handle_init(); fail_if(handle == NULL); char *urls[] = {"http://www.google.com", NULL}; lr_handle_setopt(handle, NULL, LRO_URLS, urls); lr_handle_prepare_internal_mirrorlist(handle, FALSE, &tmp_err); fail_if(tmp_err); // Prepare list of download targets tmpfn1 = lr_pathconcat(test_globals.tmpdir, "single_file_XXXXXX", NULL); mktemp(tmpfn1); fd1 = open(tmpfn1, O_RDWR|O_CREAT|O_TRUNC, 0666); lr_free(tmpfn1); fail_if(fd1 < 0); t1 = lr_downloadtarget_new(handle, "index.html", NULL, fd1, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, FALSE, FALSE); fail_if(!t1); list = g_slist_append(list, t1); // Download ret = lr_download(list, FALSE, &err); fail_if(!ret); fail_if(err); lr_handle_free(handle); // Check results for (GSList *elem = list; elem; elem = g_slist_next(elem)) { LrDownloadTarget *dtarget = elem->data; if (dtarget->err) { printf("Error msg: %s\n", dtarget->err); ck_abort(); } } g_slist_free_full(list, (GDestroyNotify) lr_downloadtarget_free); } END_TEST START_TEST(test_downloader_single_file_2) { int ret; GSList *list = NULL; GError *err = NULL; int fd1; char *tmpfn1; LrDownloadTarget *t1; // Prepare list of download targets tmpfn1 = lr_pathconcat(test_globals.tmpdir, "single_file_2_XXXXXX", NULL); mktemp(tmpfn1); fd1 = open(tmpfn1, O_RDWR|O_CREAT|O_TRUNC, 0666); lr_free(tmpfn1); fail_if(fd1 < 0); t1 = lr_downloadtarget_new(NULL, "http://seznam.cz/index.html", NULL, fd1, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, FALSE, FALSE); fail_if(!t1); list = g_slist_append(list, t1); // Download ret = lr_download(list, FALSE, &err); fail_if(!ret); fail_if(err); // Check results for (GSList *elem = list; elem; elem = g_slist_next(elem)) { LrDownloadTarget *dtarget = elem->data; if (dtarget->err) { printf("Error msg: %s\n", dtarget->err); ck_abort(); } } g_slist_free_full(list, (GDestroyNotify) lr_downloadtarget_free); } END_TEST START_TEST(test_downloader_two_files) { int ret; LrHandle *handle; GSList *list = NULL; GError *err = NULL; int fd1, fd2; char *tmpfn1, *tmpfn2; LrDownloadTarget *t1, *t2; GError *tmp_err = NULL; // Prepare handle handle = lr_handle_init(); fail_if(handle == NULL); char *urls[] = {"http://www.google.com", NULL}; lr_handle_setopt(handle, NULL, LRO_URLS, urls); lr_handle_prepare_internal_mirrorlist(handle, FALSE, &tmp_err); fail_if(tmp_err); // Prepare list of download targets tmpfn1 = lr_pathconcat(test_globals.tmpdir, "single_file_1_XXXXXX", NULL); tmpfn2 = lr_pathconcat(test_globals.tmpdir, "single_file_2_XXXXXX", NULL); mktemp(tmpfn1); mktemp(tmpfn2); fd1 = open(tmpfn1, O_RDWR|O_CREAT|O_TRUNC, 0666); fd2 = open(tmpfn2, O_RDWR|O_CREAT|O_TRUNC, 0666); lr_free(tmpfn1); lr_free(tmpfn2); fail_if(fd1 < 0); fail_if(fd2 < 0); t1 = lr_downloadtarget_new(handle, "index.html", NULL, fd1, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, FALSE, FALSE); fail_if(!t1); t2 = lr_downloadtarget_new(handle, "index.html", "http://seznam.cz", fd2, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, FALSE, FALSE); fail_if(!t2); list = g_slist_append(list, t1); list = g_slist_append(list, t2); // Download ret = lr_download(list, FALSE, &err); fail_if(!ret); fail_if(err); lr_handle_free(handle); // Check results for (GSList *elem = list; elem; elem = g_slist_next(elem)) { LrDownloadTarget *dtarget = elem->data; if (dtarget->err) { printf("Error msg: %s\n", dtarget->err); ck_abort(); } } g_slist_free_full(list, (GDestroyNotify) lr_downloadtarget_free); } END_TEST START_TEST(test_downloader_three_files_with_error) { int ret; LrHandle *handle; GSList *list = NULL; GError *err = NULL; int fd1, fd2, fd3; char *tmpfn1, *tmpfn2, *tmpfn3; LrDownloadTarget *t1, *t2, *t3; GError *tmp_err = NULL; // Prepare handle handle = lr_handle_init(); fail_if(handle == NULL); char *urls[] = {"http://www.google.com", NULL}; lr_handle_setopt(handle, NULL, LRO_URLS, urls); lr_handle_prepare_internal_mirrorlist(handle, FALSE, &tmp_err); fail_if(tmp_err); // Prepare list of download targets tmpfn1 = lr_pathconcat(test_globals.tmpdir, "single_file_1_XXXXXX", NULL); tmpfn2 = lr_pathconcat(test_globals.tmpdir, "single_file_2_XXXXXX", NULL); tmpfn3 = lr_pathconcat(test_globals.tmpdir, "single_file_3_XXXXXX", NULL); mktemp(tmpfn1); mktemp(tmpfn2); mktemp(tmpfn3); fd1 = open(tmpfn1, O_RDWR|O_CREAT|O_TRUNC, 0666); fd2 = open(tmpfn2, O_RDWR|O_CREAT|O_TRUNC, 0666); fd3 = open(tmpfn3, O_RDWR|O_CREAT|O_TRUNC, 0666); lr_free(tmpfn1); lr_free(tmpfn2); lr_free(tmpfn3); fail_if(fd1 < 0); fail_if(fd2 < 0); fail_if(fd3 < 0); t1 = lr_downloadtarget_new(handle, "index.html", NULL, fd1, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, FALSE, FALSE); fail_if(!t1); t2 = lr_downloadtarget_new(handle, "index.html", "http://seznam.cz", fd2, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, FALSE, FALSE); fail_if(!t2); t3 = lr_downloadtarget_new(handle, "i_hope_this_page_doesnt_exists.html", "http://google.com", fd3, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, FALSE, FALSE); fail_if(!t3); list = g_slist_append(list, t1); list = g_slist_append(list, t2); list = g_slist_append(list, t3); // Download ret = lr_download(list, FALSE, &err); fail_if(!ret); fail_if(err); lr_handle_free(handle); // Check results int x = 0; for (GSList *elem = list; elem; elem = g_slist_next(elem)) { LrDownloadTarget *dtarget = elem->data; ++x; if (x != 3 && dtarget->err) { printf("Error msg: %s\n", dtarget->err); ck_abort(); } if (x == 3 && !dtarget->err) { printf("No 404 error raised!\n"); ck_abort(); } } g_slist_free_full(list, (GDestroyNotify) lr_downloadtarget_free); } END_TEST START_TEST(test_downloader_checksum) { const struct { const char *sha512; int expect_err; } tests[] = { { "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", 0, }, { "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 1, }, { NULL } }; int i; for (i = 0; tests[i].sha512; i++) { int ret; LrHandle *handle; GSList *list = NULL; GError *err = NULL; int fd1; char *tmpfn1; LrDownloadTargetChecksum *checksum; GSList *checksums = NULL; LrDownloadTarget *t1; GError *tmp_err = NULL; // Prepare handle handle = lr_handle_init(); fail_if(handle == NULL); char *urls[] = {"file:///", NULL}; lr_handle_setopt(handle, NULL, LRO_URLS, urls); lr_handle_prepare_internal_mirrorlist(handle, FALSE, &tmp_err); fail_if(tmp_err); // Prepare list of download targets tmpfn1 = lr_pathconcat(test_globals.tmpdir, "single_file_XXXXXX", NULL); mktemp(tmpfn1); fd1 = open(tmpfn1, O_RDWR|O_CREAT|O_TRUNC, 0666); lr_free(tmpfn1); fail_if(fd1 < 0); checksum = lr_downloadtargetchecksum_new(LR_CHECKSUM_SHA512, tests[i].sha512); checksums = g_slist_append(checksums, checksum); t1 = lr_downloadtarget_new(handle, "dev/null", NULL, fd1, NULL, checksums, 0, 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, FALSE, FALSE); fail_if(!t1); list = g_slist_append(list, t1); // Download ret = lr_download(list, FALSE, &err); fail_if(!ret); fail_if(err); lr_handle_free(handle); // Check results for (GSList *elem = list; elem; elem = g_slist_next(elem)) { LrDownloadTarget *dtarget = elem->data; if (!tests[i].expect_err) { if (dtarget->err) { printf("Error msg: %s\n", dtarget->err); ck_abort(); } } else { if (!dtarget->err) { printf("No checksum error raised!\n"); ck_abort(); } } } g_slist_free_full(list, (GDestroyNotify) lr_downloadtarget_free); } } END_TEST Suite * downloader_suite(void) { Suite *s = suite_create("downloader"); TCase *tc = tcase_create("Main"); tcase_add_test(tc, test_downloader_no_list); tcase_add_test(tc, test_downloader_single_file); tcase_add_test(tc, test_downloader_single_file_2); tcase_add_test(tc, test_downloader_two_files); tcase_add_test(tc, test_downloader_three_files_with_error); tcase_add_test(tc, test_downloader_checksum); suite_add_tcase(s, tc); return s; } librepo-1.12.1/tests/test_downloader.h000066400000000000000000000001661372027637700177610ustar00rootroot00000000000000#ifndef LR_TEST_DOWNLOADER_H #define LR_TEST_DOWNLOADER_H #include Suite *downloader_suite(void); #endif librepo-1.12.1/tests/test_gpg.c000066400000000000000000000072671372027637700164040ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include #include #include "librepo/rcodes.h" #include "librepo/util.h" #include "librepo/gpg.h" #include "fixtures.h" #include "testsys.h" #include "test_gpg.h" START_TEST(test_gpg_check_signature) { gboolean ret; char *key_path, *_key_path; char *data_path, *_data_path; char *signature_path, *_signature_path; char *tmp_home_path; GError *tmp_err = NULL; tmp_home_path = lr_gettmpdir(); key_path = lr_pathconcat(test_globals.testdata_dir, "repo_yum_01/repodata/repomd.xml.key", NULL); _key_path = lr_pathconcat(test_globals.testdata_dir, "repo_yum_01/repodata/repomd.xml_bad.key", NULL); data_path = lr_pathconcat(test_globals.testdata_dir, "repo_yum_01/repodata/repomd.xml", NULL); _data_path = lr_pathconcat(test_globals.testdata_dir, "repo_yum_01/repodata/repomd.xml_bad", NULL); signature_path = lr_pathconcat(test_globals.testdata_dir, "repo_yum_01/repodata/repomd.xml.asc", NULL); _signature_path = lr_pathconcat(test_globals.testdata_dir, "repo_yum_01/repodata/repomd.xml_bad.asc", NULL); ret = lr_gpg_import_key(key_path, tmp_home_path, &tmp_err); fail_if(!ret); fail_if(tmp_err); // Valid key and data ret = lr_gpg_check_signature(signature_path, data_path, tmp_home_path, &tmp_err); fail_if(!ret); fail_if(tmp_err); // Bad signature signed with unknown key ret = lr_gpg_check_signature(_signature_path, data_path, tmp_home_path, &tmp_err); fail_if(ret); fail_if(!tmp_err); g_error_free(tmp_err); tmp_err = NULL; // Bad data ret = lr_gpg_check_signature(signature_path, _data_path, tmp_home_path, &tmp_err); fail_if(ret); fail_if(!tmp_err); g_error_free(tmp_err); tmp_err = NULL; // Import the 2nd key ret = lr_gpg_import_key(_key_path, tmp_home_path, &tmp_err); fail_if(!ret); fail_if(tmp_err); // Valid key and data ret = lr_gpg_check_signature(_signature_path, _data_path, tmp_home_path, &tmp_err); fail_if(!ret); fail_if(tmp_err); // Bad signature signed with known key ret = lr_gpg_check_signature(_signature_path, data_path, tmp_home_path, &tmp_err); fail_if(ret); fail_if(!tmp_err); g_error_free(tmp_err); tmp_err = NULL; // Bad data 2 ret = lr_gpg_check_signature(_signature_path, data_path, tmp_home_path, &tmp_err); fail_if(ret); fail_if(!tmp_err); g_error_free(tmp_err); tmp_err = NULL; lr_remove_dir(tmp_home_path); lr_free(key_path); lr_free(_key_path); lr_free(data_path); lr_free(_data_path); lr_free(signature_path); lr_free(_signature_path); lr_free(tmp_home_path); } END_TEST Suite * gpg_suite(void) { Suite *s = suite_create("gpg"); TCase *tc = tcase_create("Main"); tcase_add_test(tc, test_gpg_check_signature); suite_add_tcase(s, tc); return s; } librepo-1.12.1/tests/test_gpg.h000066400000000000000000000001411372027637700163710ustar00rootroot00000000000000#ifndef LR_TEST_GPG_H #define LR_TEST_GPG_H #include Suite *gpg_suite(void); #endif librepo-1.12.1/tests/test_handle.c000066400000000000000000000111461372027637700170510ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include #include #include "librepo/librepo.h" #include "librepo/rcodes.h" #include "librepo/handle.h" #include "librepo/url_substitution.h" #include "fixtures.h" #include "testsys.h" #include "test_handle.h" START_TEST(test_handle) { LrHandle *h = NULL; GError *tmp_err = NULL; h = lr_handle_init(); fail_if(h == NULL); lr_handle_free(h); h = NULL; /* This test is meant to check memory leaks. (Use valgrind) */ h = lr_handle_init(); char *urls[] = {"foo", NULL}; fail_if(!lr_handle_setopt(h, &tmp_err, LRO_URLS, urls)); fail_if(tmp_err); fail_if(!lr_handle_setopt(h, &tmp_err, LRO_URLS, urls)); fail_if(tmp_err); fail_if(!lr_handle_setopt(h, &tmp_err, LRO_MIRRORLIST, "foo")); fail_if(tmp_err); fail_if(!lr_handle_setopt(h, &tmp_err, LRO_MIRRORLIST, "bar")); fail_if(tmp_err); fail_if(!lr_handle_setopt(h, NULL, LRO_USERPWD, "user:pwd")); fail_if(!lr_handle_setopt(h, NULL, LRO_PROXY, "proxy")); fail_if(!lr_handle_setopt(h, NULL, LRO_PROXYUSERPWD, "proxyuser:pwd")); fail_if(!lr_handle_setopt(h, NULL, LRO_DESTDIR, "foodir")); fail_if(!lr_handle_setopt(h, NULL, LRO_USERAGENT, "librepo/0.0")); char *dlist[] = {"primary", "filelists", NULL}; fail_if(!lr_handle_setopt(h, NULL, LRO_YUMDLIST, dlist)); fail_if(!lr_handle_setopt(h, NULL, LRO_YUMBLIST, dlist)); LrUrlVars *vars = NULL; vars = lr_urlvars_set(vars, "foo", "bar"); fail_if(!lr_handle_setopt(h, NULL, LRO_VARSUB, vars)); fail_if(!lr_handle_setopt(h, NULL, LRO_FASTESTMIRRORCACHE, "/var/cache/fastestmirror.librepo")); fail_if(!lr_handle_setopt(h, NULL, LRO_SSLCLIENTCERT, "/etc/cert.pem")); fail_if(!lr_handle_setopt(h, NULL, LRO_SSLCLIENTKEY, "/etc/cert.key")); fail_if(!lr_handle_setopt(h, NULL, LRO_SSLCACERT, "/etc/ca.pem")); fail_if(!lr_handle_setopt(h, NULL, LRO_HTTPAUTHMETHODS, LR_AUTH_NTLM)); fail_if(!lr_handle_setopt(h, NULL, LRO_PROXYAUTHMETHODS, LR_AUTH_DIGEST)); lr_handle_free(h); } END_TEST START_TEST(test_handle_getinfo) { long num; char *str; char **strlist; LrHandle *h = NULL; GError *tmp_err = NULL; h = lr_handle_init(); num = -1; fail_if(!lr_handle_getinfo(h, &tmp_err, LRI_UPDATE, &num)); fail_if(num != 0); fail_if(tmp_err); strlist = NULL; fail_if(!lr_handle_getinfo(h, &tmp_err, LRI_URLS, &strlist)); fail_if(strlist != NULL); fail_if(tmp_err); str = NULL; fail_if(!lr_handle_getinfo(h, NULL, LRI_MIRRORLIST, &str)); fail_if(str != NULL); num = -1; fail_if(!lr_handle_getinfo(h, NULL, LRI_LOCAL, &num)); fail_if(num != 0); str = NULL; fail_if(!lr_handle_getinfo(h, NULL, LRI_DESTDIR, &str)); fail_if(str != NULL); num = -1; fail_if(!lr_handle_getinfo(h, NULL, LRI_REPOTYPE, &num)); fail_if(num != 0); str = NULL; fail_if(!lr_handle_getinfo(h, NULL, LRI_USERAGENT, &str)); fail_if(str != NULL); strlist = NULL; fail_if(!lr_handle_getinfo(h, NULL, LRI_YUMDLIST, &strlist)); fail_if(strlist != NULL); strlist = NULL; fail_if(!lr_handle_getinfo(h, NULL, LRI_YUMBLIST, &strlist)); fail_if(strlist != NULL); num = -1; fail_if(!lr_handle_getinfo(h, NULL, LRI_MAXMIRRORTRIES, &num)); fail_if(num != 0); LrUrlVars *vars = NULL; fail_if(!lr_handle_getinfo(h, NULL, LRI_VARSUB, &vars)); fail_if(strlist != NULL); str = NULL; fail_if(!lr_handle_getinfo(h, NULL, LRI_FASTESTMIRRORCACHE, &str)); fail_if(str != NULL); num = -1; fail_if(!lr_handle_getinfo(h, NULL, LRI_FASTESTMIRRORMAXAGE, &num)); fail_if(num != LRO_FASTESTMIRRORMAXAGE_DEFAULT); str = NULL; fail_if(!lr_handle_getinfo(h, NULL, LRI_SSLCLIENTCERT, &str)); fail_if(str != NULL); str = NULL; fail_if(!lr_handle_getinfo(h, NULL, LRI_SSLCLIENTKEY, &str)); fail_if(str != NULL); str = NULL; fail_if(!lr_handle_getinfo(h, NULL, LRI_SSLCACERT, &str)); fail_if(str != NULL); LrAuth auth = LR_AUTH_NONE; fail_if(!lr_handle_getinfo(h, NULL, LRI_HTTPAUTHMETHODS, &auth)); fail_if(auth != LR_AUTH_BASIC); auth = LR_AUTH_NONE; fail_if(!lr_handle_getinfo(h, NULL, LRI_PROXYAUTHMETHODS, &auth)); fail_if(auth != LR_AUTH_BASIC); lr_handle_free(h); } END_TEST Suite * handle_suite(void) { Suite *s = suite_create("handle"); TCase *tc = tcase_create("Main"); tcase_add_test(tc, test_handle); tcase_add_test(tc, test_handle_getinfo); suite_add_tcase(s, tc); return s; } librepo-1.12.1/tests/test_handle.h000066400000000000000000000001521372027637700170510ustar00rootroot00000000000000#ifndef LR_TEST_HANDLE_H #define LR_TEST_HANDLE_H #include Suite *handle_suite(void); #endif librepo-1.12.1/tests/test_lrmirrorlist.c000066400000000000000000000166521372027637700203710ustar00rootroot00000000000000#include "testsys.h" #include "test_lrmirrorlist.h" #include "librepo/lrmirrorlist.h" START_TEST(test_lrmirrorlist_append_url) { LrInternalMirrorlist *iml = NULL; LrInternalMirror *mirror = NULL; LrUrlVars *vars = NULL; gchar *url1 = g_strdup("ftp://bar"); gchar *url2 = g_strdup("http://foo"); gchar *url3 = g_strdup("http://xyz/$arch/"); gchar *var = g_strdup("arch"); gchar *val = g_strdup("i386"); vars = lr_urlvars_set(vars, var, val); g_free(var); g_free(val); iml = lr_lrmirrorlist_append_url(iml, url1, NULL); iml = lr_lrmirrorlist_append_url(iml, url2, NULL); iml = lr_lrmirrorlist_append_url(iml, url3, vars); g_free(url1); g_free(url2); g_free(url3); lr_urlvars_free(vars); mirror = lr_lrmirrorlist_nth(iml, 0); fail_if(!mirror); fail_if(strcmp(mirror->url, "ftp://bar")); mirror = lr_lrmirrorlist_nth(iml, 1); fail_if(!mirror); fail_if(strcmp(mirror->url, "http://foo")); mirror = lr_lrmirrorlist_nth(iml, 2); fail_if(!mirror); fail_if(strcmp(mirror->url, "http://xyz/i386/")); lr_lrmirrorlist_free(iml); } END_TEST START_TEST(test_lrmirrorlist_append_mirrorlist) { LrInternalMirrorlist *iml = NULL; LrInternalMirror *mirror = NULL; char *url = NULL; LrMirrorlist ml = { .urls = NULL, }; ml.urls = g_slist_prepend(ml.urls, "ftp://bar"); ml.urls = g_slist_prepend(ml.urls, "http://foo"); fail_if(g_slist_length(iml) != 0); iml = lr_lrmirrorlist_append_mirrorlist(iml, NULL, NULL); fail_if(g_slist_length(iml) != 0); iml = lr_lrmirrorlist_append_mirrorlist(iml, &ml, NULL); fail_if(g_slist_length(iml) != 2); mirror = lr_lrmirrorlist_nth(iml, 0); fail_if(!mirror); fail_if(strcmp(mirror->url, "http://foo")); fail_if(mirror->preference != 100); fail_if(mirror->protocol != LR_PROTOCOL_HTTP); mirror = lr_lrmirrorlist_nth(iml, 1); fail_if(!mirror); fail_if(strcmp(mirror->url, "ftp://bar")); fail_if(mirror->preference != 100); fail_if(mirror->protocol != LR_PROTOCOL_FTP); fail_if(g_slist_length(iml) != 2); url = lr_lrmirrorlist_nth_url(iml, 0); fail_if(strcmp(url, "http://foo")); url = lr_lrmirrorlist_nth_url(iml, 1); fail_if(strcmp(url, "ftp://bar")); lr_lrmirrorlist_free(iml); g_slist_free(ml.urls); } END_TEST START_TEST(test_lrmirrorlist_append_metalink) { LrInternalMirrorlist *iml = NULL; LrInternalMirror *mirror = NULL; char *url = NULL; LrMetalinkUrl url1 = { .protocol = "http", .type = "http", .location = "CZ", .preference = 100, .url = "http://foo/repodata/repomd.xml", }; LrMetalinkUrl url2 = { .protocol = "rsync", .type = "rsync", .location = "US", .preference = 50, .url = "", }; LrMetalinkUrl url3 = { .protocol = "ftp", .type = "ftp", .location = "CZ", .preference = 1, .url = NULL, }; LrMetalinkUrl url4 = { .protocol = "ftp", .type = "ftp", .location = "US", .preference = 95, .url = "ftp://bar/repodata/repomd.xml", }; LrMetalink ml = { .filename = NULL, .timestamp = 1, .size = 1, .hashes = NULL, .urls = NULL, }; ml.urls = g_slist_prepend(ml.urls, &url4); ml.urls = g_slist_prepend(ml.urls, &url3); ml.urls = g_slist_prepend(ml.urls, &url2); ml.urls = g_slist_prepend(ml.urls, &url1); fail_if(g_slist_length(iml) != 0); iml = lr_lrmirrorlist_append_metalink(iml, NULL, NULL, NULL); fail_if(g_slist_length(iml) != 0); iml = lr_lrmirrorlist_append_metalink(iml, &ml, "/repodata/repomd.xml", NULL); fail_if(g_slist_length(iml) != 2); // 2 because element with empty url shoud be skipped mirror = lr_lrmirrorlist_nth(iml, 0); fail_if(strcmp(mirror->url, "http://foo")); fail_if(mirror->preference != 100); fail_if(mirror->protocol != LR_PROTOCOL_HTTP); mirror = lr_lrmirrorlist_nth(iml, 1); fail_if(strcmp(mirror->url, "ftp://bar")); fail_if(mirror->preference != 95); fail_if(mirror->protocol != LR_PROTOCOL_FTP); fail_if(g_slist_length(iml) != 2); url = lr_lrmirrorlist_nth_url(iml, 0); fail_if(strcmp(url, "http://foo")); url = lr_lrmirrorlist_nth_url(iml, 1); fail_if(strcmp(url, "ftp://bar")); lr_lrmirrorlist_free(iml); // Try append on list with existing element iml = NULL; fail_if(g_slist_length(iml) != 0); iml = lr_lrmirrorlist_append_url(iml, "http://abc", NULL); fail_if(g_slist_length(iml) != 1); iml = lr_lrmirrorlist_append_metalink(iml, &ml, "/repodata/repomd.xml", NULL); fail_if(g_slist_length(iml) != 3); url = lr_lrmirrorlist_nth_url(iml, 0); fail_if(strcmp(url, "http://abc")); url = lr_lrmirrorlist_nth_url(iml, 1); fail_if(strcmp(url, "http://foo")); url = lr_lrmirrorlist_nth_url(iml, 2); fail_if(strcmp(url, "ftp://bar")); lr_lrmirrorlist_free(iml); g_slist_free(ml.urls); } END_TEST START_TEST(test_lrmirrorlist_append_lrmirrorlist) { LrInternalMirrorlist *iml = NULL, *iml_2 = NULL; LrInternalMirror *mirror = NULL; char *url = NULL; iml_2 = lr_lrmirrorlist_append_url(iml_2, "http://foo", NULL); iml_2 = lr_lrmirrorlist_append_url(iml_2, "", NULL); iml_2 = lr_lrmirrorlist_append_url(iml_2, NULL, NULL); iml_2 = lr_lrmirrorlist_append_url(iml_2, "ftp://bar", NULL); fail_if(g_slist_length(iml_2) != 2); fail_if(g_slist_length(iml) != 0); iml = lr_lrmirrorlist_append_lrmirrorlist(iml, NULL); fail_if(g_slist_length(iml) != 0); iml = lr_lrmirrorlist_append_lrmirrorlist(iml, iml_2); fail_if(g_slist_length(iml) != 2); // 2 because element with empty url shoud be skipped mirror = lr_lrmirrorlist_nth(iml, 0); fail_if(strcmp(mirror->url, "http://foo")); fail_if(mirror->preference != 100); fail_if(mirror->protocol != LR_PROTOCOL_HTTP); mirror = lr_lrmirrorlist_nth(iml, 1); fail_if(strcmp(mirror->url, "ftp://bar")); fail_if(mirror->preference != 100); fail_if(mirror->protocol != LR_PROTOCOL_FTP); fail_if(g_slist_length(iml) != 2); url = lr_lrmirrorlist_nth_url(iml, 0); fail_if(strcmp(url, "http://foo")); url = lr_lrmirrorlist_nth_url(iml, 1); fail_if(strcmp(url, "ftp://bar")); lr_lrmirrorlist_free(iml); // Try append on list with existing element iml = NULL; fail_if(g_slist_length(iml) != 0); iml = lr_lrmirrorlist_append_url(iml, "http://abc", NULL); fail_if(g_slist_length(iml) != 1); iml = lr_lrmirrorlist_append_lrmirrorlist(iml, iml_2); fail_if(g_slist_length(iml) != 3); url = lr_lrmirrorlist_nth_url(iml, 0); fail_if(strcmp(url, "http://abc")); url = lr_lrmirrorlist_nth_url(iml, 1); fail_if(strcmp(url, "http://foo")); url = lr_lrmirrorlist_nth_url(iml, 2); fail_if(strcmp(url, "ftp://bar")); lr_lrmirrorlist_free(iml); lr_lrmirrorlist_free(iml_2); } END_TEST Suite * lrmirrorlist_suite(void) { Suite *s = suite_create("internal_mirrorlist"); TCase *tc = tcase_create("Main"); tcase_add_test(tc, test_lrmirrorlist_append_url); tcase_add_test(tc, test_lrmirrorlist_append_mirrorlist); tcase_add_test(tc, test_lrmirrorlist_append_metalink); tcase_add_test(tc, test_lrmirrorlist_append_lrmirrorlist); suite_add_tcase(s, tc); return s; } librepo-1.12.1/tests/test_lrmirrorlist.h000066400000000000000000000001741372027637700203660ustar00rootroot00000000000000#ifndef LR_TEST_LRMIRRORLIST_H #define LR_TEST_LRMIRRORLIST_H #include Suite *lrmirrorlist_suite(void); #endif librepo-1.12.1/tests/test_main.c000066400000000000000000000063721372027637700165470ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include #include #include "librepo/util.h" #include "fixtures.h" #include "test_checksum.h" #include "test_downloader.h" #include "test_gpg.h" #include "test_handle.h" #include "test_lrmirrorlist.h" #include "test_metalink.h" #include "test_mirrorlist.h" #include "test_package_downloader.h" #include "test_repoconf.h" #include "test_repomd.h" #include "test_url_substitution.h" #include "test_util.h" #include "test_version.h" #include "testsys.h" static int init_test_globals(struct TestGlobals_s *tg, const char *testdata_dir) { tg->testdata_dir = lr_pathconcat(testdata_dir, "/", NULL); tg->tmpdir = g_strdup(UNITTEST_DIR); if (mkdtemp(tg->tmpdir) == NULL) return 1; return 0; } static void free_test_globals(struct TestGlobals_s *tg) { lr_free(tg->tmpdir); lr_free(tg->testdata_dir); } void print_help(const char *prog) { fprintf(stderr, "usage: %s [-v] [-d] \n", prog); fprintf(stderr, \ "\n"\ "-v Verbose output.\n"\ "-d Do downloading tests (Needs internet connection).\n"\ "\n"); } int main(int argc, char **argv) { int c; int verbose = 0; int downloading = 0; int number_failed; struct stat s; const char *repos_dir; while ((c = getopt (argc, argv, "vd")) != -1) switch (c) { case 'v': verbose = 1; break; case 'd': downloading = 1; break; default: print_help(argv[0]); exit(1); } if ((argc - optind) != 1) { print_help(argv[0]); exit(1); } repos_dir = argv[optind]; if (verbose) { // Setup logging g_log_set_handler("librepo", G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_WARNING, test_log_handler_cb, NULL); } if (stat(repos_dir, &s) || !S_ISDIR(s.st_mode)) { fprintf(stderr, "can not read repos at '%s'.\n", repos_dir); exit(1); } if (init_test_globals(&test_globals, repos_dir)) { fprintf(stderr, "failed initializing test engine.\n"); exit(1); } printf("Tests using directory: %s\n", test_globals.tmpdir); SRunner *sr = srunner_create(checksum_suite()); if (downloading) { srunner_add_suite(sr, downloader_suite()); } srunner_add_suite(sr, gpg_suite()); srunner_add_suite(sr, handle_suite()); srunner_add_suite(sr, lrmirrorlist_suite()); srunner_add_suite(sr, metalink_suite()); srunner_add_suite(sr, mirrorlist_suite()); srunner_add_suite(sr, package_downloader_suite()); srunner_add_suite(sr, repoconf_suite()); srunner_add_suite(sr, repomd_suite()); srunner_add_suite(sr, url_substitution_suite()); srunner_add_suite(sr, util_suite()); srunner_add_suite(sr, version_suite()); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); free_test_globals(&test_globals); return (number_failed == 0) ? 0 : 1; } librepo-1.12.1/tests/test_metalink.c000066400000000000000000000371001372027637700174200ustar00rootroot00000000000000#include #include #include #include #include "testsys.h" #include "fixtures.h" #include "test_metalink.h" #include "librepo/rcodes.h" #include "librepo/types.h" #include "librepo/metalink.h" #include "librepo/util.h" #define REPOMD "repomd.xml" #define METALINK_DIR "metalinks" /* metalink_good_* * This metalink files shoud be always parsed without problems. * metalink_bad_* * This metalink files are no valid, but parser could/should be * able parse it without returning (raising) an error. * But this behaviour could change in future releases. * metalink_really_bad_* * This files are invalid and shoudn't and parsing shoud raise an error. **/ START_TEST(test_metalink_init) { LrMetalink *ml = NULL; ml = lr_metalink_init(); fail_if(ml == NULL); lr_metalink_free(ml); } END_TEST START_TEST(test_metalink_good_01) { int fd; gboolean ret; char *path; LrMetalink *ml = NULL; LrMetalinkHash *mlhash = NULL; LrMetalinkUrl *mlurl = NULL; GSList *elem = NULL; GError *tmp_err = NULL; path = lr_pathconcat(test_globals.testdata_dir, METALINK_DIR, "metalink_good_01", NULL); fd = open(path, O_RDONLY); lr_free(path); fail_if(fd < 0); ml = lr_metalink_init(); fail_if(ml == NULL); ret = lr_metalink_parse_file(ml, fd, REPOMD, NULL, NULL, &tmp_err); fail_if(!ret); fail_if(tmp_err); close(fd); fail_if(ml->filename == NULL); fail_if(strcmp(ml->filename, "repomd.xml")); fail_if(ml->timestamp != 1337942396); fail_if(ml->size != 4309); fail_if(g_slist_length(ml->hashes) != 4); fail_if(g_slist_length(ml->urls) != 106); elem = g_slist_nth(ml->hashes, 0); fail_if(!elem); mlhash = elem->data; fail_if(!mlhash); fail_if(mlhash->type == NULL); fail_if(strcmp(mlhash->type, "md5")); fail_if(mlhash->value == NULL); fail_if(strcmp(mlhash->value, "20b6d77930574ae541108e8e7987ad3f")); elem = g_slist_nth(ml->hashes, 1); fail_if(!elem); mlhash = elem->data; fail_if(!mlhash); fail_if(mlhash->type == NULL); fail_if(strcmp(mlhash->type, "sha1")); fail_if(mlhash->value == NULL); fail_if(strcmp(mlhash->value, "4a5ae1831a567b58e2e0f0de1529ca199d1d8319")); elem = g_slist_nth(ml->hashes, 2); fail_if(!elem); mlhash = elem->data; fail_if(!mlhash); fail_if(mlhash->type == NULL); fail_if(strcmp(mlhash->type, "sha256")); fail_if(mlhash->value == NULL); fail_if(strcmp(mlhash->value, "0076c44aabd352da878d5c4d794901ac87f66afac869488f6a4ef166de018cdf")); elem = g_slist_nth(ml->hashes, 3); fail_if(!elem); mlhash = elem->data; fail_if(!mlhash); fail_if(mlhash->type == NULL); fail_if(strcmp(mlhash->type, "sha512")); fail_if(mlhash->value == NULL); fail_if(strcmp(mlhash->value, "884dc465da67fee8fe3f11dab321a99d9a13b22ce97f84ceff210e82b6b1a8c635ccd196add1dd738807686714c3a0a048897e2d0650bc05302b3ee26de521fd")); elem = g_slist_nth(ml->urls, 0); fail_if(!elem); mlurl = elem->data; fail_if(!mlurl); fail_if(mlurl->protocol == NULL); fail_if(strcmp(mlurl->protocol, "http")); fail_if(mlurl->type == NULL); fail_if(strcmp(mlurl->type, "http")); fail_if(mlurl->location == NULL); fail_if(strcmp(mlurl->location, "US")); fail_if(mlurl->preference != 99); fail_if(mlurl->url == NULL); fail_if(strcmp(mlurl->url, "http://mirror.pnl.gov/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml")); elem = g_slist_nth(ml->urls, 2); fail_if(!elem); mlurl = elem->data; fail_if(!mlurl); fail_if(mlurl->protocol == NULL); fail_if(strcmp(mlurl->protocol, "ftp")); fail_if(mlurl->type == NULL); fail_if(strcmp(mlurl->type, "ftp")); fail_if(mlurl->location == NULL); fail_if(strcmp(mlurl->location, "US")); fail_if(mlurl->preference != 98); fail_if(mlurl->url == NULL); fail_if(strcmp(mlurl->url, "ftp://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml")); elem = g_slist_nth(ml->urls, 104); fail_if(!elem); mlurl = elem->data; fail_if(!mlurl); fail_if(mlurl->protocol == NULL); fail_if(strcmp(mlurl->protocol, "rsync")); fail_if(mlurl->type == NULL); fail_if(strcmp(mlurl->type, "rsync")); fail_if(mlurl->location == NULL); fail_if(strcmp(mlurl->location, "CA")); fail_if(mlurl->preference != 48); fail_if(mlurl->url == NULL); fail_if(strcmp(mlurl->url, "rsync://mirror.csclub.uwaterloo.ca/fedora-enchilada/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml")); lr_metalink_free(ml); } END_TEST START_TEST(test_metalink_good_02) { int fd; gboolean ret; char *path; LrMetalink *ml = NULL; GError *tmp_err = NULL; path = lr_pathconcat(test_globals.testdata_dir, METALINK_DIR, "metalink_good_02", NULL); fd = open(path, O_RDONLY); lr_free(path); fail_if(fd < 0); ml = lr_metalink_init(); fail_if(ml == NULL); ret = lr_metalink_parse_file(ml, fd, REPOMD, NULL, NULL, &tmp_err); fail_if(!ret); fail_if(tmp_err); close(fd); fail_if(ml->filename == NULL); fail_if(strcmp(ml->filename, "repomd.xml")); fail_if(ml->timestamp != 0); fail_if(ml->size != 0); fail_if(g_slist_length(ml->hashes) != 0); fail_if(g_slist_length(ml->urls) != 3); GSList *list = g_slist_nth(ml->urls, 0); fail_if(!list); LrMetalinkUrl *mlurl = list->data; fail_if(!mlurl); fail_if(mlurl->protocol == NULL); fail_if(strcmp(mlurl->protocol, "http")); fail_if(mlurl->type == NULL); fail_if(strcmp(mlurl->type, "http")); fail_if(mlurl->location == NULL); fail_if(strcmp(mlurl->location, "US")); fail_if(mlurl->preference != 97); fail_if(mlurl->url == NULL); fail_if(strcmp(mlurl->url, "http://mirror.pnl.gov/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml")); lr_metalink_free(ml); } END_TEST START_TEST(test_metalink_good_03) { int fd; gboolean ret; char *path; LrMetalink *ml = NULL; GError *tmp_err = NULL; path = lr_pathconcat(test_globals.testdata_dir, METALINK_DIR, "metalink_good_03", NULL); fd = open(path, O_RDONLY); lr_free(path); fail_if(fd < 0); ml = lr_metalink_init(); fail_if(ml == NULL); ret = lr_metalink_parse_file(ml, fd, REPOMD, NULL, NULL, &tmp_err); fail_if(!ret); fail_if(tmp_err); close(fd); fail_if(ml->filename == NULL); fail_if(strcmp(ml->filename, "repomd.xml")); fail_if(ml->timestamp != 0); fail_if(ml->size != 0); fail_if(g_slist_length(ml->hashes) != 0); fail_if(g_slist_length(ml->urls) != 0); lr_metalink_free(ml); } END_TEST static int warning_cb(LrXmlParserWarningType type G_GNUC_UNUSED, char *msg G_GNUC_UNUSED, void *cbdata, GError **err G_GNUC_UNUSED) { *((int *) cbdata) += 1; return LR_CB_RET_OK; } START_TEST(test_metalink_bad_01) { int fd; gboolean ret; char *path; LrMetalink *ml = NULL; LrMetalinkHash *mlhash = NULL; LrMetalinkUrl *mlurl = NULL; GSList *elem = NULL; GError *tmp_err = NULL; path = lr_pathconcat(test_globals.testdata_dir, METALINK_DIR, "metalink_bad_01", NULL); fd = open(path, O_RDONLY); lr_free(path); fail_if(fd < 0); ml = lr_metalink_init(); fail_if(ml == NULL); int call_counter = 0; ret = lr_metalink_parse_file(ml, fd, REPOMD, warning_cb, &call_counter, &tmp_err); fail_if(!ret); fail_if(tmp_err); fail_if(call_counter <= 0); close(fd); fail_if(ml->filename == NULL); fail_if(strcmp(ml->filename, "repomd.xml")); fail_if(ml->timestamp != 0); fail_if(ml->size != 0); fail_if(g_slist_length(ml->hashes) != 4); fail_if(g_slist_length(ml->urls) != 4); fail_if(g_slist_length(ml->alternates) != 0); elem = g_slist_nth(ml->hashes, 0); fail_if(!elem); mlhash = elem->data; fail_if(!mlhash); fail_if(mlhash->type == NULL); fail_if(strcmp(mlhash->type, "md5")); fail_if(mlhash->value == NULL); fail_if(strcmp(mlhash->value, "20b6d77930574ae541108e8e7987ad3f")); elem = g_slist_nth(ml->hashes, 1); fail_if(!elem); mlhash = elem->data; fail_if(!mlhash); fail_if(mlhash->type == NULL); fail_if(strcmp(mlhash->type, "foo")); fail_if(mlhash->value == NULL); fail_if(strcmp(mlhash->value, "")); elem = g_slist_nth(ml->hashes, 2); fail_if(!elem); mlhash = elem->data; fail_if(!mlhash); fail_if(mlhash->type == NULL); fail_if(strcmp(mlhash->type, "sha256")); fail_if(mlhash->value == NULL); fail_if(strcmp(mlhash->value, "0076c44aabd352da878d5c4d794901ac87f66afac869488f6a4ef166de018cdf")); elem = g_slist_nth(ml->hashes, 3); fail_if(!elem); mlhash = elem->data; fail_if(!mlhash); fail_if(mlhash->type == NULL); fail_if(strcmp(mlhash->type, "sha512")); fail_if(mlhash->value == NULL); fail_if(strcmp(mlhash->value, "884dc465da67fee8fe3f11dab321a99d9a13b22ce97f84ceff210e82b6b1a8c635ccd196add1dd738807686714c3a0a048897e2d0650bc05302b3ee26de521fd")); elem = g_slist_nth(ml->urls, 0); fail_if(!elem); mlurl = elem->data; fail_if(!mlurl); fail_if(mlurl->protocol == NULL); fail_if(strcmp(mlurl->protocol, "http")); fail_if(mlurl->type == NULL); fail_if(strcmp(mlurl->type, "http")); fail_if(mlurl->location == NULL); fail_if(strcmp(mlurl->location, "US")); fail_if(mlurl->preference != 0); fail_if(mlurl->url == NULL); fail_if(strcmp(mlurl->url, "http://mirror.pnl.gov/fedora/linux/releases/17/Everything/x86_64/os/repodata/repomd.xml")); elem = g_slist_nth(ml->urls, 1); fail_if(!elem); mlurl = elem->data; fail_if(!mlurl); fail_if(mlurl->protocol != NULL); fail_if(mlurl->type != NULL); fail_if(mlurl->location != NULL); fail_if(mlurl->preference < 0 || mlurl->preference > 100); fail_if(mlurl->url == NULL); fail_if(strcmp(mlurl->url, "ftp://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml")); elem = g_slist_nth(ml->urls, 2); fail_if(!elem); mlurl = elem->data; fail_if(!mlurl); fail_if(mlurl->protocol != NULL); fail_if(mlurl->type != NULL); fail_if(mlurl->location != NULL); fail_if(mlurl->preference != 0); fail_if(mlurl->url == NULL); fail_if(strcmp(mlurl->url, "rsync://mirrors.syringanetworks.net/fedora/releases/17/Everything/x86_64/os/repodata/repomd.xml")); elem = g_slist_nth(ml->urls, 3); fail_if(!elem); mlurl = elem->data; fail_if(!mlurl); fail_if(mlurl->protocol != NULL); fail_if(mlurl->type != NULL); fail_if(mlurl->location != NULL); fail_if(mlurl->preference != 0); fail_if(mlurl->url == NULL); fail_if(strcmp(mlurl->url, "")); lr_metalink_free(ml); } END_TEST START_TEST(test_metalink_bad_02) { int fd; gboolean ret; char *path; LrMetalink *ml = NULL; GError *tmp_err = NULL; path = lr_pathconcat(test_globals.testdata_dir, METALINK_DIR, "metalink_bad_02", NULL); fd = open(path, O_RDONLY); lr_free(path); fail_if(fd < 0); ml = lr_metalink_init(); fail_if(ml == NULL); ret = lr_metalink_parse_file(ml, fd, REPOMD, NULL, NULL, &tmp_err); fail_if(!ret); fail_if(tmp_err); close(fd); fail_if(g_slist_length(ml->urls) != 0); lr_metalink_free(ml); } END_TEST START_TEST(test_metalink_really_bad_01) { int fd; gboolean ret; char *path; LrMetalink *ml = NULL; GError *tmp_err = NULL; path = lr_pathconcat(test_globals.testdata_dir, METALINK_DIR, "metalink_really_bad_01", NULL); fd = open(path, O_RDONLY); lr_free(path); fail_if(fd < 0); ml = lr_metalink_init(); fail_if(ml == NULL); ret = lr_metalink_parse_file(ml, fd, REPOMD, NULL, NULL, &tmp_err); fail_if(ret); fail_if(!tmp_err); g_error_free(tmp_err); close(fd); lr_metalink_free(ml); } END_TEST START_TEST(test_metalink_really_bad_02) { int fd; gboolean ret; char *path; LrMetalink *ml = NULL; GError *tmp_err = NULL; path = lr_pathconcat(test_globals.testdata_dir, METALINK_DIR, "metalink_really_bad_02", NULL); fd = open(path, O_RDONLY); lr_free(path); fail_if(fd < 0); ml = lr_metalink_init(); fail_if(ml == NULL); ret = lr_metalink_parse_file(ml, fd, REPOMD, NULL, NULL, &tmp_err); fail_if(ret); fail_if(!tmp_err); g_error_free(tmp_err); close(fd); lr_metalink_free(ml); } END_TEST START_TEST(test_metalink_really_bad_03) { int fd; gboolean ret; char *path; LrMetalink *ml = NULL; GError *tmp_err = NULL; path = lr_pathconcat(test_globals.testdata_dir, METALINK_DIR, "metalink_really_bad_03", NULL); fd = open(path, O_RDONLY); lr_free(path); fail_if(fd < 0); ml = lr_metalink_init(); fail_if(ml == NULL); ret = lr_metalink_parse_file(ml, fd, REPOMD, NULL, NULL, &tmp_err); fail_if(ret); fail_if(!tmp_err); g_error_free(tmp_err); close(fd); lr_metalink_free(ml); } END_TEST START_TEST(test_metalink_with_alternates) { int fd; gboolean ret; char *path; LrMetalink *ml = NULL; GSList *elem = NULL; LrMetalinkHash *mlhash = NULL; LrMetalinkAlternate *malternate = NULL; GError *tmp_err = NULL; path = lr_pathconcat(test_globals.testdata_dir, METALINK_DIR, "metalink_with_alternates", NULL); fd = open(path, O_RDONLY); lr_free(path); fail_if(fd < 0); ml = lr_metalink_init(); fail_if(ml == NULL); ret = lr_metalink_parse_file(ml, fd, REPOMD, NULL, NULL, &tmp_err); fail_if(!ret); fail_if(tmp_err); close(fd); fail_if(ml->filename == NULL); fail_if(strcmp(ml->filename, "repomd.xml")); fail_if(g_slist_length(ml->hashes) != 4); fail_if(g_slist_length(ml->alternates) != 1); elem = g_slist_nth(ml->hashes, 0); fail_if(!elem); mlhash = elem->data; fail_if(!mlhash); fail_if(mlhash->type == NULL); fail_if(strcmp(mlhash->type, "md5")); fail_if(mlhash->value == NULL); fail_if(strcmp(mlhash->value, "0ffcd7798421c9a6760f3e4202cc4675")); elem = g_slist_nth(ml->alternates, 0); fail_if(!elem); malternate = elem->data; fail_if(malternate->timestamp != 1381706941); fail_if(malternate->size != 4761); fail_if(g_slist_length(malternate->hashes) != 4); elem = g_slist_nth(malternate->hashes, 0); mlhash = elem->data; fail_if(!mlhash); fail_if(mlhash->type == NULL); fail_if(strcmp(mlhash->type, "md5")); fail_if(mlhash->value == NULL); fail_if(strcmp(mlhash->value, "0c5b64d395d5364633df7c8e97a07fd6")); lr_metalink_free(ml); } END_TEST Suite * metalink_suite(void) { Suite *s = suite_create("metalink"); TCase *tc = tcase_create("Main"); tcase_add_test(tc, test_metalink_init); tcase_add_test(tc, test_metalink_good_01); tcase_add_test(tc, test_metalink_good_02); tcase_add_test(tc, test_metalink_good_03); tcase_add_test(tc, test_metalink_bad_01); tcase_add_test(tc, test_metalink_bad_02); tcase_add_test(tc, test_metalink_really_bad_01); tcase_add_test(tc, test_metalink_really_bad_02); tcase_add_test(tc, test_metalink_really_bad_03); tcase_add_test(tc, test_metalink_with_alternates); suite_add_tcase(s, tc); return s; } librepo-1.12.1/tests/test_metalink.h000066400000000000000000000001601372027637700174210ustar00rootroot00000000000000#ifndef LR_TEST_METALINK_H #define LR_TEST_METALINK_H #include Suite *metalink_suite(void); #endif librepo-1.12.1/tests/test_mirrorlist.c000066400000000000000000000054001372027637700200200ustar00rootroot00000000000000#include #include #include #include #include "testsys.h" #include "fixtures.h" #include "test_mirrorlist.h" #include "librepo/rcodes.h" #include "librepo/types.h" #include "librepo/mirrorlist.h" #include "librepo/util.h" #define MIRRORLIST_DIR "mirrorlists" START_TEST(test_mirrorlist_init) { LrMirrorlist *ml = NULL; ml = lr_mirrorlist_init(); fail_if(ml == NULL); lr_mirrorlist_free(ml); } END_TEST START_TEST(test_mirrorlist_01) { int fd; gboolean ret; char *path; GSList *elem = NULL; LrMirrorlist *ml = NULL; GError *tmp_err = NULL; path = lr_pathconcat(test_globals.testdata_dir, MIRRORLIST_DIR, "mirrorlist_01", NULL); fd = open(path, O_RDONLY); lr_free(path); fail_if(fd < 0); ml = lr_mirrorlist_init(); fail_if(ml == NULL); ret = lr_mirrorlist_parse_file(ml, fd, &tmp_err); close(fd); fail_if(!ret); fail_if(tmp_err); fail_if(g_slist_length(ml->urls) != 2); elem = g_slist_nth(ml->urls, 0); fail_if(!elem); fail_if(g_strcmp0(elem->data, "http://foo.bar/fedora/linux/")); elem = g_slist_nth(ml->urls, 1); fail_if(!elem); fail_if(g_strcmp0(elem->data, "ftp://ftp.bar.foo/Fedora/17/")); lr_mirrorlist_free(ml); } END_TEST START_TEST(test_mirrorlist_02) { int fd; gboolean ret; char *path; LrMirrorlist *ml = NULL; GError *tmp_err = NULL; path = lr_pathconcat(test_globals.testdata_dir, MIRRORLIST_DIR, "mirrorlist_02", NULL); fd = open(path, O_RDONLY); lr_free(path); fail_if(fd < 0); ml = lr_mirrorlist_init(); fail_if(ml == NULL); ret = lr_mirrorlist_parse_file(ml, fd, &tmp_err); close(fd); fail_if(!ret); fail_if(tmp_err); fail_if(g_slist_length(ml->urls) != 0); lr_mirrorlist_free(ml); } END_TEST START_TEST(test_mirrorlist_03) { int fd; gboolean ret; char *path; LrMirrorlist *ml = NULL; GError *tmp_err = NULL; path = lr_pathconcat(test_globals.testdata_dir, MIRRORLIST_DIR, "mirrorlist_03", NULL); fd = open(path, O_RDONLY); lr_free(path); fail_if(fd < 0); ml = lr_mirrorlist_init(); fail_if(ml == NULL); ret = lr_mirrorlist_parse_file(ml, fd, &tmp_err); close(fd); fail_if(!ret); fail_if(tmp_err); fail_if(g_slist_length(ml->urls) != 0); lr_mirrorlist_free(ml); } END_TEST Suite * mirrorlist_suite(void) { Suite *s = suite_create("mirrorlist"); TCase *tc = tcase_create("Main"); tcase_add_test(tc, test_mirrorlist_init); tcase_add_test(tc, test_mirrorlist_01); tcase_add_test(tc, test_mirrorlist_02); tcase_add_test(tc, test_mirrorlist_03); suite_add_tcase(s, tc); return s; } librepo-1.12.1/tests/test_mirrorlist.h000066400000000000000000000001661372027637700200310ustar00rootroot00000000000000#ifndef LR_TEST_MIRRORLIST_H #define LR_TEST_MIRRORLIST_H #include Suite *mirrorlist_suite(void); #endif librepo-1.12.1/tests/test_package_downloader.c000066400000000000000000000040601372027637700214240ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "fixtures.h" #include "testsys.h" #include "test_package_downloader.h" #include "librepo/librepo.h" #include "librepo/rcodes.h" #include "librepo/package_downloader.h" START_TEST(test_package_downloader_new_and_free) { LrPackageTarget *target; GError *err = NULL; // Init with only basic options target = lr_packagetarget_new(NULL, "url", NULL, 0, NULL, 0, NULL, FALSE, NULL, NULL, &err); fail_if(!target); fail_if(err); fail_if(strcmp(target->relative_url, "url")); fail_if(target->dest); fail_if(target->base_url); fail_if(target->checksum_type != 0); fail_if(target->checksum); fail_if(target->resume != FALSE); fail_if(target->progresscb); fail_if(target->cbdata); fail_if(target->local_path); fail_if(target->err); lr_packagetarget_free(target); target = NULL; // Init with all options target = lr_packagetarget_new(NULL, "url", "dest", LR_CHECKSUM_SHA384, "xxx", 0, "baseurl", TRUE, (LrProgressCb) 22, (void *) 33, &err); fail_if(!target); fail_if(err); fail_if(strcmp(target->relative_url, "url")); fail_if(strcmp(target->dest, "dest")); fail_if(strcmp(target->base_url, "baseurl")); fail_if(target->checksum_type != LR_CHECKSUM_SHA384); fail_if(strcmp(target->checksum, "xxx")); fail_if(target->resume != TRUE); fail_if(target->progresscb != (LrProgressCb) 22); fail_if(target->cbdata != (void *) 33); fail_if(target->local_path); fail_if(target->err); lr_packagetarget_free(target); target = NULL; } END_TEST Suite * package_downloader_suite(void) { Suite *s = suite_create("package_downloader"); TCase *tc = tcase_create("Main"); tcase_add_test(tc, test_package_downloader_new_and_free); suite_add_tcase(s, tc); return s; } librepo-1.12.1/tests/test_package_downloader.h000066400000000000000000000002161372027637700214300ustar00rootroot00000000000000#ifndef LR_TEST_PACKAGE_DOWNLOADER_H #define LR_TEST_PACKAGE_DOWNLOADER_H #include Suite *package_downloader_suite(void); #endif librepo-1.12.1/tests/test_repo_zck.c000066400000000000000000000026231372027637700174320ustar00rootroot00000000000000#include #include #include #include #include "testsys.h" #include "fixtures.h" #include "test_repo_zck.h" #include "librepo/rcodes.h" #include "librepo/types.h" #include "librepo/repomd.h" #include "librepo/util.h" START_TEST(test_repo_zck_parsing) { int fd; gboolean ret; LrYumRepoMd *repomd; char *repomd_path; GError *tmp_err = NULL; repomd_path = lr_pathconcat(test_globals.testdata_dir, "repo_yum_03/repodata/repomd.xml", NULL); repomd = lr_yum_repomd_init(); fail_if(!repomd); fd = open(repomd_path, O_RDONLY); fail_if(fd < 0); ret = lr_yum_repomd_parse_file(repomd, fd, NULL, NULL, &tmp_err); close(fd); fail_if(!ret); fail_if(tmp_err); fail_if(g_slist_length(repomd->records) != 12); fail_if(!lr_yum_repomd_get_record(repomd, "primary")); fail_if(!lr_yum_repomd_get_record(repomd, "filelists")); fail_if(!lr_yum_repomd_get_record(repomd, "other")); fail_if(lr_yum_repomd_get_record(repomd, "foo")); fail_if(lr_yum_repomd_get_record(repomd, "bar")); lr_yum_repomd_free(repomd); lr_free(repomd_path); } END_TEST Suite * repo_zck_suite(void) { Suite *s = suite_create("repo_zck"); TCase *tc = tcase_create("Main"); tcase_add_test(tc, test_repo_zck_parsing); suite_add_tcase(s, tc); return s; } librepo-1.12.1/tests/test_repo_zck.h000066400000000000000000000001601372027637700174310ustar00rootroot00000000000000#ifndef LR_TEST_REPO_ZCK_H #define LR_TEST_REPO_ZCK_H #include Suite *repo_zck_suite(void); #endif librepo-1.12.1/tests/test_repoconf.c000066400000000000000000000551641372027637700174410ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include "testsys.h" #include "fixtures.h" #include "test_repomd.h" #include "librepo/rcodes.h" #include "librepo/types.h" #include "librepo/repoconf.h" #include "librepo/util.h" #include "librepo/cleanup.h" static void repoconf_assert_true(LrYumRepoConf *repoconf, LrYumRepoConfOption option) { ck_assert(1); long val = 0L; _cleanup_error_free_ GError *tmp_err = NULL; gboolean ret = lr_yum_repoconf_getinfo(repoconf, &tmp_err, option, &val); ck_assert(!tmp_err); ck_assert(ret); ck_assert_msg(val != 0, "Not a True value (Option %d)", option); } #define conf_assert_true(option) \ repoconf_assert_true(conf, (option)) static void repoconf_assert_false(LrYumRepoConf *repoconf, LrYumRepoConfOption option) { ck_assert(1); long val = 1L; _cleanup_error_free_ GError *tmp_err = NULL; gboolean ret = lr_yum_repoconf_getinfo(repoconf, &tmp_err, option, &val); ck_assert(!tmp_err); ck_assert(ret); ck_assert_msg(!val, "Not a 0 (False) value (Option %d)", option); } #define conf_assert_false(option) \ repoconf_assert_false(conf, (option)) static void repoconf_assert_na(LrYumRepoConf *repoconf, LrYumRepoConfOption option) { ck_assert(1); char array[2048]; /* ^^^XXX the lr_yum_repoconf_getinfo always expects that you have * passed an argument of an appropriate size and even when an error * occurs, it can write a value (e.g. default value) into the * destination. Because we don't care about the value and the size * are different for different types (char*, gint64, long, ...) * we pass in an array with (hopefully) enough space to accommodate * all reasonable return values. */ _cleanup_error_free_ GError *tmp_err = NULL; gboolean ret = lr_yum_repoconf_getinfo(repoconf, &tmp_err, option, array); ck_assert(!ret); ck_assert(tmp_err != NULL); ck_assert(tmp_err->code == LRE_NOTSET); } #define conf_assert_na(option) \ repoconf_assert_na(conf, (option)) static void repoconf_assert_str_eq(LrYumRepoConf *repoconf, LrYumRepoConfOption option, gchar *expected) { ck_assert(1); _cleanup_free_ gchar *str = NULL; _cleanup_error_free_ GError *tmp_err = NULL; gboolean ret = lr_yum_repoconf_getinfo(repoconf, &tmp_err, option, &str); ck_assert(!tmp_err); ck_assert(ret); ck_assert(g_strcmp0(str, expected) == 0); } #define conf_assert_str_eq(option, expected) \ repoconf_assert_str_eq(conf, (option), (expected)) static void repoconf_assert_uint64_eq(LrYumRepoConf *repoconf, LrYumRepoConfOption option, guint64 expected) { ck_assert(1); guint64 val = (expected - 1); _cleanup_error_free_ GError *tmp_err = NULL; gboolean ret = lr_yum_repoconf_getinfo(repoconf, &tmp_err, option, &val); ck_assert(!tmp_err); ck_assert(ret); ck_assert(val == expected); } #define conf_assert_uint64_eq(option, expected) \ repoconf_assert_uint64_eq(conf, (option), (expected)) static void repoconf_assert_int_eq(LrYumRepoConf *repoconf, LrYumRepoConfOption option, gint expected) { ck_assert(1); gint val = (expected - 1); _cleanup_error_free_ GError *tmp_err = NULL; gboolean ret = lr_yum_repoconf_getinfo(repoconf, &tmp_err, option, &val); ck_assert(!tmp_err); ck_assert(ret); ck_assert_int_eq(val, expected); } #define conf_assert_int_eq(option, expected) \ repoconf_assert_int_eq(conf, (option), (expected)) static void repoconf_assert_int64_eq(LrYumRepoConf *repoconf, LrYumRepoConfOption option, gint64 expected) { ck_assert(1); gint64 val = (expected - 1); _cleanup_error_free_ GError *tmp_err = NULL; gboolean ret = lr_yum_repoconf_getinfo(repoconf, &tmp_err, option, &val); ck_assert(!tmp_err); ck_assert(ret); ck_assert_int_eq(val, expected); } #define conf_assert_int64_eq(option, expected) \ repoconf_assert_int64_eq(conf, (option), (expected)) static void repoconf_assert_lripresolvetype_eq(LrYumRepoConf *repoconf, LrYumRepoConfOption option, LrIpResolveType expected) { ck_assert(1); LrIpResolveType val = (expected - 1); _cleanup_error_free_ GError *tmp_err = NULL; gboolean ret = lr_yum_repoconf_getinfo(repoconf, &tmp_err, option, &val); ck_assert(!tmp_err); ck_assert(ret); ck_assert_msg((val == expected), "IpResolve assert failed %d != %d", val, expected); } #define conf_assert_lripresolvetype_eq(option, expected) \ repoconf_assert_lripresolvetype_eq(conf, (option), (expected)) static void repoconf_assert_strv_eq(LrYumRepoConf *repoconf, LrYumRepoConfOption option, ...) { ck_assert(1); va_list args; _cleanup_error_free_ GError *tmp_err = NULL; _cleanup_strv_free_ char **strv = NULL; gboolean ret = lr_yum_repoconf_getinfo(repoconf, &tmp_err, option, &strv); ck_assert(!tmp_err); ck_assert(ret); ck_assert_msg(strv != NULL, "NULL isn't strv"); va_start (args, option); gchar **strv_p = strv; for (; *strv_p; strv_p++) { gchar *s = va_arg (args, gchar*); ck_assert_msg(s != NULL, "Lengths of lists are not the same"); ck_assert_str_eq(*strv_p, s); } // strv list already ended, check if we really expected the end ck_assert_msg(va_arg(args, gchar*) == NULL, "Lengths of lists are not the same"); va_end (args); } #define conf_assert_strv_eq(option, ...) \ repoconf_assert_strv_eq(conf, (option), __VA_ARGS__) static void repoconf_assert_set_boolean(LrYumRepoConf *repoconf, LrYumRepoConfOption option, gboolean val) { ck_assert(1); _cleanup_error_free_ GError *tmp_err = NULL; gboolean ret = lr_yum_repoconf_setopt(repoconf, &tmp_err, option, val); ck_assert(!tmp_err); ck_assert(ret); } #define conf_assert_set_boolean(option, val) \ repoconf_assert_set_boolean(conf, (option), (val)) static void repoconf_assert_set_str(LrYumRepoConf *repoconf, LrYumRepoConfOption option, gchar *val) { ck_assert(1); _cleanup_error_free_ GError *tmp_err = NULL; gboolean ret = lr_yum_repoconf_setopt(repoconf, &tmp_err, option, val); ck_assert(!tmp_err); ck_assert(ret); } #define conf_assert_set_str(option, val) \ repoconf_assert_set_str(conf, (option), (val)) static void repoconf_assert_set_strv(LrYumRepoConf *repoconf, LrYumRepoConfOption option, ...) { ck_assert(1); va_list args; _cleanup_error_free_ GError *tmp_err = NULL; _cleanup_ptrarray_unref_ GPtrArray *array = g_ptr_array_new(); // Build strv from VA args va_start (args, option); for (gchar *s=va_arg(args, gchar*); s; s=va_arg(args, gchar*)) g_ptr_array_add(array, s); g_ptr_array_add(array, NULL); va_end (args); // Set the option gboolean ret = lr_yum_repoconf_setopt(repoconf, &tmp_err, option, array->pdata); ck_assert(!tmp_err); ck_assert(ret); } #define conf_assert_set_strv(option, ...) \ repoconf_assert_set_strv(conf, (option), __VA_ARGS__) static void repoconf_assert_set_int(LrYumRepoConf *repoconf, LrYumRepoConfOption option, gint val) { ck_assert(1); _cleanup_error_free_ GError *tmp_err = NULL; gboolean ret = lr_yum_repoconf_setopt(repoconf, &tmp_err, option, val); ck_assert(!tmp_err); ck_assert(ret); } #define conf_assert_set_int(option, val) \ repoconf_assert_set_int(conf, (option), (val)) static void repoconf_assert_set_int64(LrYumRepoConf *repoconf, LrYumRepoConfOption option, gint64 val) { ck_assert(1); _cleanup_error_free_ GError *tmp_err = NULL; gboolean ret = lr_yum_repoconf_setopt(repoconf, &tmp_err, option, val); ck_assert(!tmp_err); ck_assert(ret); } #define conf_assert_set_int64(option, val) \ repoconf_assert_set_int64(conf, (option), (val)) static void repoconf_assert_set_uint64(LrYumRepoConf *repoconf, LrYumRepoConfOption option, guint64 val) { ck_assert(1); _cleanup_error_free_ GError *tmp_err = NULL; gboolean ret = lr_yum_repoconf_setopt(repoconf, &tmp_err, option, val); ck_assert(!tmp_err); ck_assert(ret); } #define conf_assert_set_uint64(option, val) \ repoconf_assert_set_uint64(conf, (option), (val)) static void repoconf_assert_set_lripresolvetype(LrYumRepoConf *repoconf, LrYumRepoConfOption option, LrIpResolveType val) { ck_assert(1); _cleanup_error_free_ GError *tmp_err = NULL; gboolean ret = lr_yum_repoconf_setopt(repoconf, &tmp_err, option, val); ck_assert(!tmp_err); ck_assert(ret); } #define conf_assert_set_lripresolvetype(option, val) \ repoconf_assert_set_lripresolvetype(conf, (option), (val)) START_TEST(test_parse_repoconf_minimal) { gboolean ret; LrYumRepoConf *conf = NULL; LrYumRepoConfs *confs = NULL; GSList *list = NULL; _cleanup_free_ gchar *path = NULL; _cleanup_error_free_ GError *tmp_err = NULL; path = lr_pathconcat(test_globals.testdata_dir, "repo-minimal.repo", NULL); confs = lr_yum_repoconfs_init(); fail_if(!confs); ret = lr_yum_repoconfs_parse(confs, path, &tmp_err); fail_if(!ret); list = lr_yum_repoconfs_get_list(confs, &tmp_err); fail_if(!list); fail_if(g_slist_length(list) != 2); // Test content of first repo config conf = g_slist_nth_data(list, 0); fail_if(!conf); conf_assert_str_eq(LR_YRC_ID, "minimal-repo-1"); conf_assert_str_eq(LR_YRC_NAME, "Minimal repo 1 - $basearch"); conf_assert_na(LR_YRC_ENABLED); conf_assert_strv_eq(LR_YRC_BASEURL, "http://m1.com/linux/$basearch", NULL); conf_assert_na(LR_YRC_MIRRORLIST); conf_assert_na(LR_YRC_METALINK); conf_assert_na(LR_YRC_MEDIAID); conf_assert_na(LR_YRC_GPGKEY); conf_assert_na(LR_YRC_GPGCAKEY); conf_assert_na(LR_YRC_EXCLUDE); conf_assert_na(LR_YRC_INCLUDE); conf_assert_na(LR_YRC_FASTESTMIRROR); conf_assert_na(LR_YRC_PROXY); conf_assert_na(LR_YRC_PROXY_USERNAME); conf_assert_na(LR_YRC_PROXY_PASSWORD); conf_assert_na(LR_YRC_USERNAME); conf_assert_na(LR_YRC_PASSWORD); conf_assert_na(LR_YRC_GPGCHECK); conf_assert_na(LR_YRC_REPO_GPGCHECK); conf_assert_na(LR_YRC_ENABLEGROUPS); conf_assert_na(LR_YRC_BANDWIDTH); conf_assert_na(LR_YRC_THROTTLE); conf_assert_na(LR_YRC_IP_RESOLVE); conf_assert_na(LR_YRC_METADATA_EXPIRE); conf_assert_na(LR_YRC_COST); conf_assert_na(LR_YRC_PRIORITY); conf_assert_na(LR_YRC_SSLCACERT); conf_assert_na(LR_YRC_SSLVERIFY); conf_assert_na(LR_YRC_SSLCLIENTCERT); conf_assert_na(LR_YRC_SSLCLIENTKEY); conf_assert_na(LR_YRC_DELTAREPOBASEURL); conf_assert_na(LR_YRC_FAILOVERMETHOD); conf_assert_na(LR_YRC_SKIP_IF_UNAVAILABLE); // Test content of second repo config conf = g_slist_nth_data(list, 1); fail_if(!conf); conf_assert_str_eq(LR_YRC_ID, "minimal-repo-2"); conf_assert_str_eq(LR_YRC_NAME, "Minimal repo 2 - $basearch"); conf_assert_na(LR_YRC_ENABLED); conf_assert_strv_eq(LR_YRC_BASEURL, "http://m2.com/linux/$basearch", NULL); conf_assert_na(LR_YRC_MIRRORLIST); conf_assert_na(LR_YRC_METALINK); conf_assert_na(LR_YRC_MEDIAID); conf_assert_na(LR_YRC_GPGKEY); conf_assert_na(LR_YRC_GPGCAKEY); conf_assert_na(LR_YRC_EXCLUDE); conf_assert_na(LR_YRC_INCLUDE); conf_assert_na(LR_YRC_FASTESTMIRROR); conf_assert_na(LR_YRC_PROXY); conf_assert_na(LR_YRC_PROXY_USERNAME); conf_assert_na(LR_YRC_PROXY_PASSWORD); conf_assert_na(LR_YRC_USERNAME); conf_assert_na(LR_YRC_PASSWORD); conf_assert_na(LR_YRC_GPGCHECK); conf_assert_na(LR_YRC_REPO_GPGCHECK); conf_assert_na(LR_YRC_ENABLEGROUPS); conf_assert_na(LR_YRC_BANDWIDTH); conf_assert_na(LR_YRC_THROTTLE); conf_assert_na(LR_YRC_IP_RESOLVE); conf_assert_na(LR_YRC_METADATA_EXPIRE); conf_assert_na(LR_YRC_COST); conf_assert_na(LR_YRC_PRIORITY); conf_assert_na(LR_YRC_SSLCACERT); conf_assert_na(LR_YRC_SSLVERIFY); conf_assert_na(LR_YRC_SSLCLIENTCERT); conf_assert_na(LR_YRC_SSLCLIENTKEY); conf_assert_na(LR_YRC_FAILOVERMETHOD); conf_assert_na(LR_YRC_SKIP_IF_UNAVAILABLE); conf_assert_na(LR_YRC_DELTAREPOBASEURL); lr_yum_repoconfs_free(confs); } END_TEST START_TEST(test_parse_repoconf_big) { gboolean ret; LrYumRepoConf *conf = NULL; LrYumRepoConfs *confs = NULL; GSList *list = NULL; _cleanup_free_ gchar *path = NULL; _cleanup_error_free_ GError *tmp_err = NULL; path = lr_pathconcat(test_globals.testdata_dir, "repo-big.repo", NULL); confs = lr_yum_repoconfs_init(); fail_if(!confs); ret = lr_yum_repoconfs_parse(confs, path, &tmp_err); fail_if(!ret); list = lr_yum_repoconfs_get_list(confs, &tmp_err); fail_if(!list); fail_if(g_slist_length(list) != 1); conf = g_slist_nth_data(list, 0); fail_if(!conf); conf_assert_str_eq(LR_YRC_ID, "big-repo"); conf_assert_str_eq(LR_YRC_NAME, "Maxi repo - $basearch"); conf_assert_true(LR_YRC_ENABLED); conf_assert_strv_eq(LR_YRC_BASEURL, "http://foo1.org/pub/linux/$releasever/$basearch/os/", "ftp://ftp.foo2/pub/linux/$releasever/$basearch/os/", "https://foo3.org/pub/linux/", NULL); conf_assert_str_eq(LR_YRC_MIRRORLIST,"http://foo1.org/mirrorlist"); conf_assert_str_eq(LR_YRC_METALINK, "https://foo1.org/metalink?repo=linux-$releasever&arch=$basearch"); conf_assert_str_eq(LR_YRC_MEDIAID, "0"); conf_assert_strv_eq(LR_YRC_GPGKEY, "https://foo1.org/linux/foo_signing_key.pub", "https://foo2.org/linux/foo_signing_key.pub", NULL); conf_assert_strv_eq(LR_YRC_GPGCAKEY, "https://foo1.org/linux/ca_key.pub", NULL); conf_assert_strv_eq(LR_YRC_EXCLUDE, "package_1", "package_2", NULL); conf_assert_strv_eq(LR_YRC_INCLUDE, "package_a", "package_b", NULL); conf_assert_true(LR_YRC_FASTESTMIRROR); conf_assert_str_eq(LR_YRC_PROXY, "socks5://127.0.0.1:5000"); conf_assert_str_eq(LR_YRC_PROXY_USERNAME, "proxyuser"); conf_assert_str_eq(LR_YRC_PROXY_PASSWORD, "proxypass"); conf_assert_str_eq(LR_YRC_USERNAME, "user"); conf_assert_str_eq(LR_YRC_PASSWORD, "pass"); conf_assert_true(LR_YRC_GPGCHECK); conf_assert_true(LR_YRC_REPO_GPGCHECK); conf_assert_true(LR_YRC_ENABLEGROUPS); conf_assert_uint64_eq(LR_YRC_BANDWIDTH, G_GUINT64_CONSTANT(1048576)); // 1024*1024 conf_assert_str_eq(LR_YRC_THROTTLE, "50%"); conf_assert_lripresolvetype_eq(LR_YRC_IP_RESOLVE, LR_IPRESOLVE_V6); conf_assert_int64_eq(LR_YRC_METADATA_EXPIRE, G_GINT64_CONSTANT(432000)); // 60*60*24*5 conf_assert_int_eq(LR_YRC_COST, 500); conf_assert_int_eq(LR_YRC_PRIORITY, 10); conf_assert_str_eq(LR_YRC_SSLCACERT, "file:///etc/ssl.cert"); conf_assert_true(LR_YRC_SSLVERIFY); conf_assert_str_eq(LR_YRC_SSLCLIENTCERT, "file:///etc/client.cert"); conf_assert_str_eq(LR_YRC_SSLCLIENTKEY, "file:///etc/client.key"); conf_assert_strv_eq(LR_YRC_DELTAREPOBASEURL, "http://deltarepomirror.org/", "http://deltarepomirror2.org", NULL); conf_assert_str_eq(LR_YRC_FAILOVERMETHOD, "priority"); conf_assert_true(LR_YRC_SKIP_IF_UNAVAILABLE); lr_yum_repoconfs_free(confs); } END_TEST START_TEST(test_write_repoconf) { _cleanup_fd_close_ int rc = -1; gboolean ret; LrYumRepoConfs *confs; LrYumRepoConf * conf; char tmpfn[] = "/tmp/librepo_repoconf_test_XXXXXX"; const char *ids[] = {"test_id", NULL}; GSList *repos = NULL; _cleanup_error_free_ GError *tmp_err = NULL; // Create a temporary file rc = mkstemp(tmpfn); fail_if(rc == -1); // Create reconfs with one repoconf with one id (one section) confs = lr_yum_repoconfs_init(); ret = lr_yum_repoconfs_add_empty_conf(confs, tmpfn, ids, &tmp_err); ck_assert(ret); ck_assert(!tmp_err); // Check if the repoconf with the section was created repos = lr_yum_repoconfs_get_list(confs, &tmp_err); ck_assert(!tmp_err); ck_assert_msg(repos != NULL, "No repo has been created"); conf = g_slist_nth_data(repos, 0); // Ty to set and get options conf_assert_str_eq(LR_YRC_ID, "test_id"); conf_assert_na(LR_YRC_NAME); conf_assert_set_str(LR_YRC_NAME, "test_name"); conf_assert_str_eq(LR_YRC_NAME, "test_name"); conf_assert_na(LR_YRC_ENABLED); conf_assert_set_boolean(LR_YRC_ENABLED, TRUE); ck_assert(1); conf_assert_true(LR_YRC_ENABLED); ck_assert(1); conf_assert_na(LR_YRC_BASEURL); conf_assert_set_strv(LR_YRC_BASEURL, "test_baseurl1", "test_baseurl2", NULL); conf_assert_strv_eq(LR_YRC_BASEURL, "test_baseurl1", "test_baseurl2", NULL); conf_assert_na(LR_YRC_MIRRORLIST); conf_assert_set_str(LR_YRC_MIRRORLIST, "test_mirrorlist"); conf_assert_str_eq(LR_YRC_MIRRORLIST, "test_mirrorlist"); conf_assert_na(LR_YRC_METALINK); conf_assert_set_str(LR_YRC_METALINK, "test_metalink"); conf_assert_str_eq(LR_YRC_METALINK, "test_metalink"); conf_assert_na(LR_YRC_MEDIAID); conf_assert_set_str(LR_YRC_MEDIAID, "test_mediaid"); conf_assert_str_eq(LR_YRC_MEDIAID, "test_mediaid"); conf_assert_na(LR_YRC_GPGKEY); conf_assert_set_strv(LR_YRC_GPGKEY, "test_gpgkey", NULL); conf_assert_strv_eq(LR_YRC_GPGKEY, "test_gpgkey", NULL); conf_assert_na(LR_YRC_GPGCAKEY); conf_assert_set_strv(LR_YRC_GPGCAKEY, "test_gpgcakey", NULL); conf_assert_strv_eq(LR_YRC_GPGCAKEY, "test_gpgcakey", NULL); conf_assert_na(LR_YRC_EXCLUDE); conf_assert_set_strv(LR_YRC_EXCLUDE, "test_exclude", NULL); conf_assert_strv_eq(LR_YRC_EXCLUDE, "test_exclude", NULL); conf_assert_na(LR_YRC_INCLUDE); conf_assert_set_strv(LR_YRC_INCLUDE, "test_include", NULL); conf_assert_strv_eq(LR_YRC_INCLUDE, "test_include", NULL); conf_assert_na(LR_YRC_FASTESTMIRROR); conf_assert_set_boolean(LR_YRC_FASTESTMIRROR, TRUE); ck_assert(1); conf_assert_true(LR_YRC_FASTESTMIRROR); ck_assert(1); conf_assert_na(LR_YRC_PROXY); conf_assert_set_str(LR_YRC_PROXY, "test_proxy"); conf_assert_str_eq(LR_YRC_PROXY, "test_proxy"); conf_assert_na(LR_YRC_PROXY_USERNAME); conf_assert_set_str(LR_YRC_PROXY_USERNAME, "test_proxy_username"); conf_assert_str_eq(LR_YRC_PROXY_USERNAME, "test_proxy_username"); conf_assert_na(LR_YRC_PROXY_PASSWORD); conf_assert_set_str(LR_YRC_PROXY_PASSWORD, "test_proxy_password"); conf_assert_str_eq(LR_YRC_PROXY_PASSWORD, "test_proxy_password"); conf_assert_na(LR_YRC_USERNAME); conf_assert_set_str(LR_YRC_USERNAME, "test_username"); conf_assert_str_eq(LR_YRC_USERNAME, "test_username"); conf_assert_na(LR_YRC_PASSWORD); conf_assert_set_str(LR_YRC_PASSWORD, "test_password"); conf_assert_str_eq(LR_YRC_PASSWORD, "test_password"); conf_assert_na(LR_YRC_GPGCHECK); conf_assert_set_boolean(LR_YRC_GPGCHECK, TRUE); ck_assert(1); conf_assert_true(LR_YRC_GPGCHECK); ck_assert(1); conf_assert_na(LR_YRC_REPO_GPGCHECK); conf_assert_set_boolean(LR_YRC_REPO_GPGCHECK, TRUE); ck_assert(1); conf_assert_true(LR_YRC_REPO_GPGCHECK); ck_assert(1); conf_assert_na(LR_YRC_ENABLEGROUPS); conf_assert_set_boolean(LR_YRC_ENABLEGROUPS, TRUE); ck_assert(1); conf_assert_true(LR_YRC_ENABLEGROUPS); ck_assert(1); conf_assert_na(LR_YRC_BANDWIDTH); conf_assert_set_uint64(LR_YRC_BANDWIDTH, G_GUINT64_CONSTANT(55)); conf_assert_uint64_eq(LR_YRC_BANDWIDTH, G_GUINT64_CONSTANT(55)); conf_assert_na(LR_YRC_THROTTLE); conf_assert_set_str(LR_YRC_THROTTLE, "test_throttle"); conf_assert_str_eq(LR_YRC_THROTTLE, "test_throttle"); conf_assert_na(LR_YRC_IP_RESOLVE); conf_assert_set_lripresolvetype(LR_YRC_IP_RESOLVE, LR_IPRESOLVE_V4); conf_assert_lripresolvetype_eq(LR_YRC_IP_RESOLVE, LR_IPRESOLVE_V4); conf_assert_na(LR_YRC_METADATA_EXPIRE); conf_assert_set_int64(LR_YRC_METADATA_EXPIRE, G_GINT64_CONSTANT(123)); conf_assert_int64_eq(LR_YRC_METADATA_EXPIRE, G_GINT64_CONSTANT(123)); conf_assert_na(LR_YRC_COST); conf_assert_set_int(LR_YRC_COST, 456); conf_assert_int_eq(LR_YRC_COST, 456); conf_assert_na(LR_YRC_PRIORITY); conf_assert_set_int(LR_YRC_PRIORITY, 789); conf_assert_int_eq(LR_YRC_PRIORITY, 789); conf_assert_na(LR_YRC_SSLCACERT); conf_assert_set_str(LR_YRC_SSLCACERT, "test_sslcacert"); conf_assert_str_eq(LR_YRC_SSLCACERT, "test_sslcacert"); conf_assert_na(LR_YRC_SSLVERIFY); conf_assert_set_boolean(LR_YRC_SSLVERIFY, TRUE); ck_assert(1); conf_assert_true(LR_YRC_SSLVERIFY); ck_assert(1); conf_assert_na(LR_YRC_SSLCLIENTCERT); conf_assert_set_str(LR_YRC_SSLCLIENTCERT, "test_sslclientcert"); conf_assert_str_eq(LR_YRC_SSLCLIENTCERT, "test_sslclientcert"); conf_assert_na(LR_YRC_SSLCLIENTKEY); conf_assert_set_str(LR_YRC_SSLCLIENTKEY, "test_sslclientkey"); conf_assert_str_eq(LR_YRC_SSLCLIENTKEY, "test_sslclientkey"); conf_assert_na(LR_YRC_DELTAREPOBASEURL); conf_assert_set_strv(LR_YRC_DELTAREPOBASEURL, "test_deltarepobaseurl", NULL); conf_assert_strv_eq(LR_YRC_DELTAREPOBASEURL, "test_deltarepobaseurl", NULL); conf_assert_na(LR_YRC_FAILOVERMETHOD); conf_assert_set_str(LR_YRC_FAILOVERMETHOD, "test_failovermethod"); conf_assert_str_eq(LR_YRC_FAILOVERMETHOD, "test_failovermethod"); conf_assert_na(LR_YRC_SKIP_IF_UNAVAILABLE); conf_assert_set_boolean(LR_YRC_SKIP_IF_UNAVAILABLE, TRUE); ck_assert(1); conf_assert_true(LR_YRC_SKIP_IF_UNAVAILABLE); ck_assert(1); // Write out modified config ret = lr_yum_repoconfs_save(confs, &tmp_err); ck_assert(!tmp_err); ck_assert(ret); // Cleanup resources lr_yum_repoconfs_free(confs); unlink(tmpfn); } END_TEST Suite * repoconf_suite(void) { Suite *s = suite_create("repoconf"); TCase *tc = tcase_create("Main"); tcase_add_test(tc, test_parse_repoconf_minimal); tcase_add_test(tc, test_parse_repoconf_big); tcase_add_test(tc, test_write_repoconf); suite_add_tcase(s, tc); return s; } librepo-1.12.1/tests/test_repoconf.h000066400000000000000000000001601372027637700174300ustar00rootroot00000000000000#ifndef LR_TEST_REPOCONF_H #define LR_TEST_REPOCONF_H #include Suite *repoconf_suite(void); #endif librepo-1.12.1/tests/test_repomd.c000066400000000000000000000036541372027637700171110ustar00rootroot00000000000000#include #include #include #include #include "testsys.h" #include "fixtures.h" #include "test_repomd.h" #include "librepo/rcodes.h" #include "librepo/types.h" #include "librepo/repomd.h" #include "librepo/util.h" START_TEST(test_repomd_parsing) { int fd; gboolean ret; LrYumRepoMd *repomd; char *repomd_path; GError *tmp_err = NULL; repomd_path = lr_pathconcat(test_globals.testdata_dir, "repo_yum_02/repodata/repomd.xml", NULL); repomd = lr_yum_repomd_init(); fail_if(!repomd); fd = open(repomd_path, O_RDONLY); fail_if(fd < 0); ret = lr_yum_repomd_parse_file(repomd, fd, NULL, NULL, &tmp_err); close(fd); fail_if(!ret); fail_if(tmp_err); fail_if(g_slist_length(repomd->records) != 12); fail_if(!lr_yum_repomd_get_record(repomd, "primary")); fail_if(!lr_yum_repomd_get_record(repomd, "filelists")); fail_if(!lr_yum_repomd_get_record(repomd, "other")); fail_if(!lr_yum_repomd_get_record(repomd, "primary_db")); fail_if(!lr_yum_repomd_get_record(repomd, "filelists_db")); fail_if(!lr_yum_repomd_get_record(repomd, "other_db")); fail_if(!lr_yum_repomd_get_record(repomd, "group")); fail_if(!lr_yum_repomd_get_record(repomd, "group_gz")); fail_if(!lr_yum_repomd_get_record(repomd, "updateinfo")); fail_if(!lr_yum_repomd_get_record(repomd, "origin")); fail_if(!lr_yum_repomd_get_record(repomd, "prestodelta")); fail_if(!lr_yum_repomd_get_record(repomd, "deltainfo")); fail_if(lr_yum_repomd_get_record(repomd, "foo")); fail_if(lr_yum_repomd_get_record(repomd, "bar")); lr_yum_repomd_free(repomd); lr_free(repomd_path); } END_TEST Suite * repomd_suite(void) { Suite *s = suite_create("repomd"); TCase *tc = tcase_create("Main"); tcase_add_test(tc, test_repomd_parsing); suite_add_tcase(s, tc); return s; } librepo-1.12.1/tests/test_repomd.h000066400000000000000000000001521372027637700171040ustar00rootroot00000000000000#ifndef LR_TEST_REPOMD_H #define LR_TEST_REPOMD_H #include Suite *repomd_suite(void); #endif librepo-1.12.1/tests/test_url_substitution.c000066400000000000000000000102611372027637700212510ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include #include #include "librepo/rcodes.h" #include "librepo/util.h" #include "librepo/url_substitution.h" #include "fixtures.h" #include "testsys.h" #include "test_url_substitution.h" START_TEST(test_urlvars_set) { LrUrlVars *urlvars = NULL; urlvars = lr_urlvars_set(urlvars, "foo", "bar"); fail_if(urlvars == NULL); fail_if(strcmp(((LrVar *)urlvars->data)->var, "foo") != 0); urlvars = lr_urlvars_set(urlvars, "foo1", "bar1"); fail_if(urlvars == NULL); urlvars = lr_urlvars_set(urlvars, "foo", NULL); fail_if(urlvars == NULL); fail_if(strcmp(((LrVar *)urlvars->data)->var, "foo1") != 0); urlvars = lr_urlvars_set(urlvars, "foo1", NULL); fail_if(urlvars != NULL); urlvars = lr_urlvars_set(urlvars, "bar", "foo"); fail_if(urlvars == NULL); fail_if(strcmp(((LrVar *)urlvars->data)->var, "bar") != 0); lr_urlvars_free(urlvars); } END_TEST START_TEST(test_url_substitute_without_urlvars) { char *url; LrUrlVars *urlvars = NULL; urlvars = lr_urlvars_set(urlvars, "foo", "bar"); url = lr_url_substitute("", urlvars); fail_if(strcmp(url, "")); lr_free(url); url = lr_url_substitute("http://foo", urlvars); fail_if(strcmp(url, "http://foo")); lr_free(url); url = lr_url_substitute("http://foo?id=$bar", urlvars); fail_if(strcmp(url, "http://foo?id=$bar")); lr_free(url); url = lr_url_substitute("http://foo?id=$foox", urlvars); fail_if(strcmp(url, "http://foo?id=$foox")); lr_free(url); lr_urlvars_free(urlvars); } END_TEST START_TEST(test_url_substitute) { char *url; LrUrlVars *urlvars = NULL; urlvars = lr_urlvars_set(urlvars, "foo", "version"); urlvars = lr_urlvars_set(urlvars, "fo", "ver"); urlvars = lr_urlvars_set(urlvars, "bar", "repo"); url = lr_url_substitute("", urlvars); fail_if(strcmp(url, "")); lr_free(url); url = lr_url_substitute("http://foo", urlvars); fail_if(strcmp(url, "http://foo")); lr_free(url); url = lr_url_substitute("http://foo?id=$bar", urlvars); fail_if(strcmp(url, "http://foo?id=repo")); lr_free(url); url = lr_url_substitute("http://$foo?id=$bar", urlvars); fail_if(strcmp(url, "http://version?id=repo")); lr_free(url); url = lr_url_substitute("http://$fo?id=$bar", urlvars); fail_if(strcmp(url, "http://ver?id=repo")); lr_free(url); url = lr_url_substitute("http://$foo$bar", urlvars); fail_if(strcmp(url, "http://versionrepo")); lr_free(url); url = lr_url_substitute("http://$foo$bar/", urlvars); fail_if(strcmp(url, "http://versionrepo/")); lr_free(url); lr_urlvars_free(urlvars); } END_TEST START_TEST(test_url_substitute_braces) { char *url; LrUrlVars *urlvars = NULL; urlvars = lr_urlvars_set(urlvars, "foo", "version"); urlvars = lr_urlvars_set(urlvars, "fo", "ver"); urlvars = lr_urlvars_set(urlvars, "bar", "repo"); url = lr_url_substitute("http://foo?id=${bar}", urlvars); fail_if(strcmp(url, "http://foo?id=repo")); lr_free(url); url = lr_url_substitute("http://${foo}?id=${bar}", urlvars); fail_if(strcmp(url, "http://version?id=repo")); lr_free(url); url = lr_url_substitute("http://${fo}?id=$bar", urlvars); fail_if(strcmp(url, "http://ver?id=repo")); lr_free(url); url = lr_url_substitute("http://${fo?id=$bar", urlvars); fail_if(strcmp(url, "http://${fo?id=repo")); lr_free(url); url = lr_url_substitute("http://${foo${bar}", urlvars); fail_if(strcmp(url, "http://${foorepo")); lr_free(url); url = lr_url_substitute("http://${foo}${bar}/", urlvars); fail_if(strcmp(url, "http://versionrepo/")); lr_free(url); lr_urlvars_free(urlvars); } END_TEST Suite * url_substitution_suite(void) { Suite *s = suite_create("url_substitution"); TCase *tc = tcase_create("Main"); tcase_add_test(tc, test_urlvars_set); tcase_add_test(tc, test_url_substitute_without_urlvars); tcase_add_test(tc, test_url_substitute); tcase_add_test(tc, test_url_substitute_braces); suite_add_tcase(s, tc); return s; } librepo-1.12.1/tests/test_url_substitution.h000066400000000000000000000002101372027637700212470ustar00rootroot00000000000000#ifndef LR_TEST_URL_SUBSTITUTION_H #define LR_TEST_URL_SUBSTITUTION_H #include Suite *url_substitution_suite(void); #endif librepo-1.12.1/tests/test_util.c000066400000000000000000000153111372027637700165710ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include #include #include "librepo/rcodes.h" #include "librepo/util.h" #include "fixtures.h" #include "testsys.h" #include "test_util.h" START_TEST(test_malloc) { long long *num = NULL; num = lr_malloc0(sizeof(long long)); fail_if(num == NULL); lr_free(num); } END_TEST START_TEST(test_malloc0) { long long *num = NULL; num = lr_malloc0(sizeof(long long)); fail_if(num == NULL); fail_if(*num != 0LL); lr_free(num); } END_TEST START_TEST(test_free) { // No SIGSEGV should be raised lr_free(NULL); } END_TEST START_TEST(test_gettmpfile) { int fd = 0; fd = lr_gettmpfile(); fail_if(fd < 0); close(fd); } END_TEST START_TEST(test_gettmpdir) { char *tmp_dir = lr_gettmpdir(); fail_if(tmp_dir == NULL); fail_if(rmdir(tmp_dir) != 0); lr_free(tmp_dir); } END_TEST START_TEST(test_pathconcat) { char *path = NULL; path = lr_pathconcat(NULL, NULL); fail_if(path != NULL); path = lr_pathconcat("", NULL); fail_if(path == NULL); fail_if(strcmp(path, "")); lr_free(path); path = NULL; path = lr_pathconcat("/tmp", "foo///", "bar", NULL); fail_if(path == NULL); fail_if(strcmp(path, "/tmp/foo/bar")); lr_free(path); path = NULL; path = lr_pathconcat("foo", "bar/", NULL); fail_if(path == NULL); fail_if(strcmp(path, "foo/bar")); lr_free(path); path = NULL; path = lr_pathconcat("foo", "/bar/", NULL); fail_if(path == NULL); fail_if(strcmp(path, "foo/bar")); lr_free(path); path = NULL; path = lr_pathconcat("foo", "bar", "", NULL); fail_if(path == NULL); fail_if(strcmp(path, "foo/bar/")); lr_free(path); path = NULL; path = lr_pathconcat("http://host.net", "path/to/somewhere", NULL); fail_if(path == NULL); fail_if(strcmp(path, "http://host.net/path/to/somewhere")); lr_free(path); path = NULL; path = lr_pathconcat("http://host.net?hello=1", "path/to/", "somewhere", NULL); fail_if(path == NULL); fail_if(strcmp(path, "http://host.net/path/to/somewhere?hello=1")); lr_free(path); path = NULL; } END_TEST START_TEST(test_remove_dir) { char *tmp_dir; char *tmp_file; int fd, rc; tmp_dir = lr_gettmpdir(); fail_if(tmp_dir == NULL); tmp_file = lr_pathconcat(tmp_dir, "file_a", NULL); fd = open(tmp_file, O_CREAT|O_TRUNC|O_RDWR, 0660); fail_if(fd < 0); close(fd); rc = lr_remove_dir(tmp_dir); fail_if(rc != 0); fail_if(unlink(tmp_file) == 0); fail_if(rmdir(tmp_dir) == 0); lr_free(tmp_dir); lr_free(tmp_file); } END_TEST START_TEST(test_url_without_path) { char *new_url = NULL; new_url = lr_url_without_path(NULL); fail_if(new_url != NULL); new_url = lr_url_without_path(""); fail_if(new_url == NULL); fail_if(strcmp(new_url, "")); lr_free(new_url); new_url = NULL; new_url = lr_url_without_path("hostname"); fail_if(new_url == NULL); fail_if(strcmp(new_url, "hostname")); lr_free(new_url); new_url = NULL; new_url = lr_url_without_path("hostname/foo/bar/"); fail_if(new_url == NULL); fail_if(strcmp(new_url, "hostname")); lr_free(new_url); new_url = NULL; new_url = lr_url_without_path("hostname:80"); fail_if(new_url == NULL); fail_if(strcmp(new_url, "hostname:80")); lr_free(new_url); new_url = NULL; new_url = lr_url_without_path("hostname:80/foo/bar"); fail_if(new_url == NULL); fail_if(strcmp(new_url, "hostname:80")); lr_free(new_url); new_url = NULL; new_url = lr_url_without_path("http://hostname:80/"); fail_if(new_url == NULL); fail_if(strcmp(new_url, "http://hostname:80")); lr_free(new_url); new_url = NULL; new_url = lr_url_without_path("http://hostname:80/foo/bar"); fail_if(new_url == NULL); fail_if(strcmp(new_url, "http://hostname:80")); lr_free(new_url); new_url = NULL; new_url = lr_url_without_path("ftp://foo.hostname:80/foo/bar"); fail_if(new_url == NULL); fail_if(strcmp(new_url, "ftp://foo.hostname:80")); lr_free(new_url); new_url = NULL; new_url = lr_url_without_path("file:///home/foobar"); fail_if(new_url == NULL); fail_if(strcmp(new_url, "file://")); lr_free(new_url); new_url = NULL; new_url = lr_url_without_path("file:/home/foobar"); fail_if(new_url == NULL); fail_if(strcmp(new_url, "file://")); lr_free(new_url); new_url = NULL; } END_TEST START_TEST(test_strv_dup) { gchar **in0 = NULL; gchar *in1[] = {NULL}; gchar *in2[] = {"foo", NULL}; gchar **copy = NULL; copy = lr_strv_dup(in0); fail_if(copy != NULL); copy = lr_strv_dup(in1); fail_if(!copy); fail_if(copy == in1); fail_if(copy[0] != NULL); g_strfreev(copy); copy = lr_strv_dup(in2); fail_if(!copy); fail_if(copy == in2); fail_if(g_strcmp0(copy[0], "foo")); fail_if(copy[0] == in2[0]); fail_if(copy[1] != NULL); g_strfreev(copy); } END_TEST START_TEST(test_is_local_path) { fail_if(!lr_is_local_path("/tmp")); fail_if(!lr_is_local_path("foo/bar")); fail_if(!lr_is_local_path("bar")); fail_if(!lr_is_local_path("/")); fail_if(!lr_is_local_path("file:///tmp")); fail_if(!lr_is_local_path("file:/tmp")); fail_if(lr_is_local_path(NULL)); fail_if(lr_is_local_path("")); fail_if(lr_is_local_path("http://foo.bar")); fail_if(lr_is_local_path("https://foo.bar/x")); fail_if(lr_is_local_path("ftp://foo.bar/foobar")); fail_if(lr_is_local_path("rsync://xyz")); } END_TEST START_TEST(test_prepend_url_protocol) { gchar *url = NULL; url = lr_prepend_url_protocol("/tmp"); fail_if(g_strcmp0(url, "file:///tmp")); g_free(url); url = lr_prepend_url_protocol("file:///tmp"); fail_if(g_strcmp0(url, "file:///tmp")); g_free(url); url = lr_prepend_url_protocol("http://tmp"); fail_if(g_strcmp0(url, "http://tmp")); g_free(url); url = lr_prepend_url_protocol("file:/tmp"); fail_if(g_strcmp0(url, "file:/tmp")); g_free(url); } END_TEST Suite * util_suite(void) { Suite *s = suite_create("util"); TCase *tc = tcase_create("Main"); tcase_add_test(tc, test_malloc); tcase_add_test(tc, test_malloc0); tcase_add_test(tc, test_free); tcase_add_test(tc, test_gettmpfile); tcase_add_test(tc, test_gettmpdir); tcase_add_test(tc, test_pathconcat); tcase_add_test(tc, test_remove_dir); tcase_add_test(tc, test_url_without_path); tcase_add_test(tc, test_strv_dup); tcase_add_test(tc, test_is_local_path); tcase_add_test(tc, test_prepend_url_protocol); suite_add_tcase(s, tc); return s; } librepo-1.12.1/tests/test_util.h000066400000000000000000000001441372027637700165740ustar00rootroot00000000000000#ifndef LR_TEST_UTIL_H #define LR_TEST_UTIL_H #include Suite *util_suite(void); #endif librepo-1.12.1/tests/test_version.c000066400000000000000000000020251372027637700172770ustar00rootroot00000000000000#include #include #include #include "librepo/version.h" #include "fixtures.h" #include "testsys.h" #include "test_version.h" START_TEST(test_version_check_macro) { fail_if(!(LR_VERSION_CHECK(LR_VERSION_MAJOR, LR_VERSION_MINOR, LR_VERSION_PATCH))); fail_if(!(LR_VERSION_CHECK(0, 0, 0))); fail_if(LR_VERSION_CHECK(LR_VERSION_MAJOR, LR_VERSION_MINOR, LR_VERSION_PATCH+1)); fail_if(LR_VERSION_CHECK(LR_VERSION_MAJOR, LR_VERSION_MINOR+1, LR_VERSION_PATCH)); fail_if(LR_VERSION_CHECK(LR_VERSION_MAJOR+1, LR_VERSION_MINOR, LR_VERSION_PATCH)); } END_TEST Suite * version_suite(void) { Suite *s = suite_create("version"); TCase *tc = tcase_create("Main"); tcase_add_test(tc, test_version_check_macro); suite_add_tcase(s, tc); return s; } librepo-1.12.1/tests/test_version.h000066400000000000000000000001551372027637700173060ustar00rootroot00000000000000#ifndef LR_TEST_VERSION_H #define LR_TEST_VERSION_H #include Suite *version_suite(void); #endif librepo-1.12.1/tests/testsys.c000066400000000000000000000002471372027637700162750ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include #include "testsys.h" librepo-1.12.1/tests/testsys.h000066400000000000000000000001351372027637700162760ustar00rootroot00000000000000#ifndef LR_TESTSYS_H #define LR_TESTSYS_H #define UNITTEST_DIR "/tmp/librepoXXXXXX" #endif librepo-1.12.1/utils/000077500000000000000000000000001372027637700144065ustar00rootroot00000000000000librepo-1.12.1/utils/FindLibrepo.cmake000066400000000000000000000021061372027637700176040ustar00rootroot00000000000000# - Try to find Librepo # Once done this will define # LIBREPO_FOUND - System has Librepo # LIBREPO_INCLUDE_DIRS - The Librepo include directories # LIBREPO_LIBRARIES - The libraries needed to use Librepo # LIBREPO_DEFINITIONS - Compiler switches required for using Librepo find_package(PkgConfig) pkg_check_modules(PC_LIBREPO QUIET librepo) set(LIBREPO_DEFINITIONS ${PC_LIBREPO_CFLAGS_OTHER}) find_path(LIBREPO_INCLUDE_DIR librepo/librepo.h HINTS ${PC_LIBREPO_INCLUDEDIR} ${PC_LIBREPO_INCLUDE_DIRS} PATH_SUFFIXES libxml2 ) find_library(LIBREPO_LIBRARY NAMES repo librepo HINTS ${PC_LIBREPO_LIBDIR} ${PC_LIBREPO_LIBRARY_DIRS} ) set(LIBREPO_LIBRARIES ${LIBREPO_LIBRARY} ) set(LIBREPO_INCLUDE_DIRS ${LIBREPO_INCLUDE_DIR} ) include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LIBREPO_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(Librepo DEFAULT_MSG LIBREPO_LIBRARY LIBREPO_INCLUDE_DIR) mark_as_advanced(LIBREPO_INCLUDE_DIR LIBREPO_LIBRARY ) librepo-1.12.1/utils/README.md000066400000000000000000000011661372027637700156710ustar00rootroot00000000000000# Utils - Some useful scripts and other stuff ## FindLibrepo.cmake CMake module for find Librepo library. [More info](http://www.cmake.org/Wiki/CMake:How_To_Find_Libraries#Using_external_libraries_that_CMake_doesn.27t_yet_have_modules_for, "CMake:How To Find Libraries") ## make_rpm.sh **Usage (from root project dir):** utils/make_rpm.sh . [git_revision] Generate tarbal and make rpm for specified git revision. Spec file librepo.spec must be available in root project dir. ## make_tarball.sh **Usage (from root project dir):** utils/make_tarball.sh [git_revision] Generate tarbal for specified git revision. librepo-1.12.1/utils/cleanup.sh000077500000000000000000000002021372027637700163660ustar00rootroot00000000000000#!/bin/bash rm -f librepo-*.tar.gz librepo-*.rpm python2-librepo-*.rpm python-librepo-*.rpm python3-librepo-*.rpm echo -e "Done" librepo-1.12.1/utils/make_rpm.sh000077500000000000000000000051601372027637700165420ustar00rootroot00000000000000#!/bin/bash PACKAGE="librepo" RPMBUILD_DIR="${HOME}/rpmbuild/" BUILD_DIR="$RPMBUILD_DIR/BUILD" GITREV=`git rev-parse --short HEAD` PREFIX="" # Root project dir MY_DIR=`dirname "$0"` SRPM_ONLY=0 USAGE_STRING="Usage: `basename "$0"` [-s|--srpm-only] [--rpmbuild-options OPTIONS]" ## Arguments sanity check if [ $# -lt "1" -o $# -gt "4" ] then echo $USAGE_STRING exit 1 fi ## Argument parser while [[ $# > 0 ]] do key="$1" case $key in -h|--help) echo $USAGE_STRING exit 0 ;; -s|--srpm-only) SRPM_ONLY=1 ;; --rpmbuild-options) BUILD_OPTS="$2" shift # past argument ;; *) # unknown option PREFIX=$1 ;; esac shift # past argument or value done if [ ! -d "$RPMBUILD_DIR" ]; then echo "rpmbuild dir $RPMBUILD_DIR doesn't exist!" echo "init rpmbuild dir with command: rpmdev-setuptree" echo "(Hint: Package group @development-tools and package fedora-packager)" exit 1 fi echo "Generating rpm for $GITREV" echo "Cleaning $BUILD_DIR" rm -rf "$BUILD_DIR" echo "Removing $RPMBUILD_DIR/$PACKAGE.spec" rm -f "$RPMBUILD_DIR/$PACKAGE.spec" echo "> Making tarball .." TARBALL=$("$MY_DIR/make_tarball.sh" "$GITREV" | grep ".tar.gz") if [ ! $? == "0" ]; then echo "Error while making tarball" exit 1 fi echo "Tarball done" echo "> Copying tarball and .spec file into the $RPMBUILD_DIR .." cp "$PREFIX/$TARBALL" "$RPMBUILD_DIR/SOURCES/" if [ ! $? == "0" ]; then echo "Error while: cp $PREFIX/$TARBALL $RPMBUILD_DIR/SOURCES/" exit 1 fi sed -i "s/%{\!?gitrev: %global gitrev .*/%{\!?gitrev: %global gitrev $GITREV}/g" "$PREFIX/$PACKAGE.spec" cp "$PREFIX/$PACKAGE.spec" "$RPMBUILD_DIR/SPECS/" if [ ! $? == "0" ]; then echo "Error while: cp $PREFIX/$PACKAGE.spec $RPMBUILD_DIR/SPECS/" exit 1 fi echo "Copying done" RPMBUILD_ACTION="-ba" if [ "$SRPM_ONLY" -eq "1" ] then RPMBUILD_ACTION="-bs" echo "> Building only srpm" fi echo "> Starting rpmbuild $PACKAGE.." rpmbuild $RPMBUILD_ACTION $BUILD_OPTS "$RPMBUILD_DIR/SPECS/$PACKAGE.spec" if [ ! $? == "0" ]; then echo "Error while: rpmbuild -ba $RPMBUILD_DIR/SPECS/$PACKAGE.spec" exit 1 fi echo "rpmbuild done" echo "> Cleanup .." rpmbuild --clean "$RPMBUILD_DIR/SPECS/$PACKAGE.spec" echo "Cleanup done" echo "> Moving srpm .." mv --verbose "$RPMBUILD_DIR"/SRPMS/"$PACKAGE"-*.src.rpm "$PREFIX/." if [ "$SRPM_ONLY" -ne "1" ] then echo "> Moving rpms .." mv --verbose "$RPMBUILD_DIR"/RPMS/*/"$PACKAGE"-*.rpm "$PREFIX/." mv --verbose "$RPMBUILD_DIR"/RPMS/*/python*-"$PACKAGE"-*.rpm "$PREFIX/." fi echo "Moving done" echo "All done!" librepo-1.12.1/utils/make_tarball.sh000077500000000000000000000007521372027637700173670ustar00rootroot00000000000000PACKAGE="librepo" TARGET_DIR="." MAJOR=$(grep MAJOR VERSION.cmake | awk -F\" '{print $2}') MINOR=$(grep MINOR VERSION.cmake | awk -F\" '{print $2}') PATCH=$(grep PATCH VERSION.cmake | awk -F\" '{print $2}') NV="$PACKAGE"-"$MAJOR.$MINOR.$PATCH" FN="$NV.tar.gz" if [ "$#" -eq "0" ]; then GITREV=`git rev-parse --short HEAD` else GITREV="$1" fi echo "Generate tarball for revision: $GITREV" git archive "${GITREV}" --prefix="$PACKAGE-$NV"/ | gzip > "$TARGET_DIR"/"$FN" echo "$FN" librepo-1.12.1/utils/single_test.py000077500000000000000000000042061372027637700173050ustar00rootroot00000000000000#!/usr/bin/env python """ Convert a single line from test output to run single test command. E.g: "2: test_download_package_via_metalink (tests.test_yum_package_downloading.TestCaseYumPackageDownloading) ... ok" To: tests/python/tests/test_yum_repo_downloading.py:TestCaseYumRepoDownloading.test_download_and_update_repo_01 """ import os import sys import argparse LIBPATH = "./build/librepo/python/python{pythonversion:d}/" COMMAND = "PYTHONPATH=`readlink -f {libpath}` nosetests{nosever} -s -v {testpath}" TEST_PATH_PREFIX = "tests/python" if __name__ == "__main__": parser = argparse.ArgumentParser(description='Convert a single line from '\ 'python unittesttest output to run command for this single test') parser.add_argument('test_out_line', metavar='TESTOUTPUTLINE', type=str, help='A single line from python unittesttest output') parser.add_argument('--python3', action='store_true', help='Python3 version') args = parser.parse_args() test_out_line = args.test_out_line # Remove suffix "... ok" or "... FAIL" test_out_line = test_out_line.split(" ... ")[0] # Remove prefix "test_number: " res = test_out_line = test_out_line.split(": ") test_out_line = res[-1] # Get test name res = test_out_line.split(" ") if len(res) != 2: print("Bad input line format") sys.exit(1) test_name, test_out_line = res # Get test case test_out_line = test_out_line.strip().lstrip("(").rstrip(")") res = test_out_line.rsplit(".", 1) if len(res) != 2: print("Bad input line format") sys.exit(1) test_out_line, test_case = res # Get path test_path = test_out_line.replace(".", "/") + ".py" full_path = os.path.join(TEST_PATH_PREFIX, test_path) nosever = "" pythonversion = 2 if (args.python3): nosever = "-3.3" pythonversion = 3 testpath = "{0}:{1}.{2}".format(full_path, test_case, test_name) libpath = LIBPATH.format(pythonversion=pythonversion) command = COMMAND.format(libpath=libpath, nosever=nosever, testpath=testpath) print(command) librepo-1.12.1/valgrind.supp000066400000000000000000000030311372027637700157620ustar00rootroot00000000000000# librepo suppressions for valgrind # # http://wiki.wxwidgets.org/Valgrind_Suppression_File_Howto # # To have valgrind use this file use --suppressions=/path/to/valgrind.supp # # Format of this file is: # { # name_of_suppression # tool_name:supp_kind # (optional extra info for some suppression types) # caller0 name, or /name/of/so/file.so # caller1 name, or ditto # (optionally: caller2 name) # (optionally: caller3 name) # } # # tool_name: # Memcheck, Addrcheck, Callgrind, ... (and other valgrind tools) # supp_kinds for Memcheck are: # Leak Free Cond Param # Value1 Value2 Value4 Value8 Value16 # Addr1 Addr2 Addr4 Addr8 Addr16 # optional extra info is: # if Param: name of system call param # if Free: name of free-ing fn # caller line starts with: # # 'fun' for function calls # 'obj' for shared object # '...' is the frame-level wildcard - matches zero or more fun: or obj: lines # # Example: # { # gdk_set_locale # Memcheck:Leak # ... # fun:gdk_set_locale # } { openssl-supp-load-compressions Memcheck:Leak fun:malloc fun:CRYPTO_malloc ... fun:load_builtin_compressions fun:SSL_COMP_get_compression_methods fun:SSL_library_init } { gpgme-static-get-engine-info Memcheck:Leak fun:malloc ... fun:gpgme_get_engine_info } { gpgme-static-set-engine-info Memcheck:Leak fun:malloc ... fun:_gpgme_set_engine_info fun:gpgme_set_engine_info } { gpgme-static-set-locale Memcheck:Leak fun:malloc fun:strdup fun:gpgme_set_locale }