commit 07d10d4d4e62f77c309788b91e6f151c16c56181 Author: Ivailo Monev Date: Tue Nov 18 17:46:34 2014 +0000 initial import of ark diff --git a/ark/.reviewboardrc b/ark/.reviewboardrc new file mode 100644 index 00000000..2056db27 --- /dev/null +++ b/ark/.reviewboardrc @@ -0,0 +1,4 @@ +REPOSITORY = "git://anongit.kde.org/ark" +REVIEWBOARD_URL = "https://git.reviewboard.kde.org" +TARGET_GROUPS = "kdeutils" +TARGET_PEOPLE = "rkcosta" diff --git a/ark/CMakeLists.txt b/ark/CMakeLists.txt new file mode 100644 index 00000000..db467038 --- /dev/null +++ b/ark/CMakeLists.txt @@ -0,0 +1,50 @@ +project(ark) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) + +if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) + find_package(KDE4) + include( KDE4Defaults ) + include_directories(${KDE4_INCLUDES}) + + add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) + add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS) + + # If definitions like -D_GNU_SOURCE are needed for these checks they + # should be added to _KDE4_PLATFORM_DEFINITIONS when it is originally + # defined outside this file. Here we include these definitions in + # CMAKE_REQUIRED_DEFINITIONS so they will be included in the build of + # checks below. + set( CMAKE_REQUIRED_DEFINITIONS ${_KDE4_PLATFORM_DEFINITIONS} ) +endif() + +include( MacroLibrary ) +list(APPEND CMAKE_MODULE_PATH ${ark_SOURCE_DIR}/cmake/modules) + +macro_optional_find_package(LibArchive) +macro_log_feature(LIBARCHIVE_FOUND "LibArchive" "A library for dealing with a wide variety of archive file formats" "http://code.google.com/p/libarchive/" FALSE "" "Required for among others tar, tar.gz, tar.bz2 formats in Ark.") + +configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CTestCustom.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake) + +add_definitions(-DQT_NO_CAST_FROM_ASCII) + +option(WITH_TEST_COVERAGE "Build with test coverage support" OFF) +if (WITH_TEST_COVERAGE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") +endif (WITH_TEST_COVERAGE) + +set(SUPPORTED_ARK_MIMETYPES "") + +add_subdirectory(plugins) +add_subdirectory(kerfuffle) +add_subdirectory(part) +add_subdirectory(app) +add_subdirectory(doc) + +if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) + message(STATUS "Supported MIME types: ${SUPPORTED_ARK_MIMETYPES}") + macro_display_feature_log() +endif(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/ark/COPYING b/ark/COPYING new file mode 100644 index 00000000..5185fd3f --- /dev/null +++ b/ark/COPYING @@ -0,0 +1,346 @@ +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde programs) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), 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 Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. 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) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; 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. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ark/COPYING.icons b/ark/COPYING.icons new file mode 100644 index 00000000..045b62e8 --- /dev/null +++ b/ark/COPYING.icons @@ -0,0 +1,216 @@ +The Oxygen Icon Theme + Copyright (C) 2007 Nuno Pinheiro + Copyright (C) 2007 David Vignoni + Copyright (C) 2007 David Miller + Copyright (C) 2007 Johann Ollivier Lapeyre + Copyright (C) 2007 Kenneth Wimer + Copyright (C) 2007 Riccardo Iaconelli + + +and others + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This 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, see . + +Clarification: + + The GNU Lesser General Public License or LGPL is written for + software libraries in the first place. We expressly want the LGPL to + be valid for this artwork library too. + + KDE Oxygen theme icons is a special kind of software library, it is an + artwork library, it's elements can be used in a Graphical User Interface, or + GUI. + + Source code, for this library means: + - where they exist, SVG; + - otherwise, if applicable, the multi-layered formats xcf or psd, or + otherwise png. + + The LGPL in some sections obliges you to make the files carry + notices. With images this is in some cases impossible or hardly useful. + + With this library a notice is placed at a prominent place in the directory + containing the elements. You may follow this practice. + + The exception in section 5 of the GNU Lesser General Public License covers + the use of elements of this art library in a GUI. + + kde-artists [at] kde.org + +----- + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/ark/CTestConfig.cmake b/ark/CTestConfig.cmake new file mode 100644 index 00000000..8bd9c156 --- /dev/null +++ b/ark/CTestConfig.cmake @@ -0,0 +1,13 @@ +## This file should be placed in the root directory of your project. +## Then modify the CMakeLists.txt file in the root directory of your +## project to incorporate the testing dashboard. +## # The following are required to uses Dart and the Cdash dashboard +## ENABLE_TESTING() +## INCLUDE(CTest) +set(CTEST_PROJECT_NAME "ark") +set(CTEST_NIGHTLY_START_TIME "20:00:00 CET") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=ark") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/ark/CTestCustom.cmake.in b/ark/CTestCustom.cmake.in new file mode 100644 index 00000000..130d3df8 --- /dev/null +++ b/ark/CTestCustom.cmake.in @@ -0,0 +1 @@ +set(CTEST_CUSTOM_COVERAGE_EXCLUDE ".moc$" "moc_" "ui_") diff --git a/ark/HACKING b/ark/HACKING new file mode 100644 index 00000000..27ef2634 --- /dev/null +++ b/ark/HACKING @@ -0,0 +1,52 @@ +== Coding Style == +Ark follows the kdelibs/Qt coding style. For more information about them, +please see: + + - http://techbase.kde.org/Policies/Kdelibs_Coding_Style + - http://wiki.qt-project.org/Coding_Style + - http://wiki.qt-project.org/Coding_Conventions + +== Sending patches == +To send patches for Ark, you can either use git's send-email command and send +it to the kde-utils-devel@kde.org mailing list, or use KDE's ReviewBoard tool +in . In case you choose the latter, the +review should be sent for the Ark product, and the `kdeutils' group should be +added to the review request. + +If you already have a KDE commit account, it is still preferrable to contact +the maintainer instead of committing directly, at least to be a good citizen +and especially so that git mistakes are not made (see the `Using git' section). + +== Using git == +The development model adopted by Ark is simple and rely on git's easy merging +and branching capabilities. If in doubt, do not hesitate to ask! + +First of all, you should do your work in a separate branch, and each commit +should be as atomic as possible. This way, if you are asked to make changes to +them, the rest of your work is not disturbed and you can easily rebase. + +New features are committed to the `master' branch, respecting KDE's Release +Schedule policies. This means the soft and hard freeze periods must be +respected, as well as the string freeze policy. + +Bug fixes are committed to the latest stable branch (for example, KDE/4.8), +which is then merged into the `master' branch. Do *NOT* cherry-pick commits +into multiple branches! It makes following history unnecessarily harder for no +reason. + +To merge the stable branch into `master', the following steps can be followed: + + $ git checkout KDE/4.8 # Whatever the stable branch is + $ # hack, hack, hack + $ # commit + $ git checkout master + $ git merge --log --edit -s recursive -Xours KDE/4.8 + +Do not worry if unrelated commits (such as translation ones made by KDE's +translation infrastructure) are also merged: translation commits are +automatically reverted when needed, and other commits being merged should be +bug fixes by definition. + +When committing your changes, do *NOT* create unnecessary merge commits with +`git pull', as these commits are completely avoidable and make following +history harder. If you are committing your changes, *rebase* first. diff --git a/ark/Mainpage.dox b/ark/Mainpage.dox new file mode 100644 index 00000000..6b9ef1e4 --- /dev/null +++ b/ark/Mainpage.dox @@ -0,0 +1,8 @@ +/** @mainpage ark + +The ark application + +*/ + +// DOXYGEN_REFERENCES = kdecore +// DOXYGEN_SET_PROJECT_NAME = ark diff --git a/ark/Messages.sh b/ark/Messages.sh new file mode 100644 index 00000000..101f99be --- /dev/null +++ b/ark/Messages.sh @@ -0,0 +1,6 @@ +#! /bin/sh +$EXTRACTRC $(find . -name '*.rc') >> rc.cpp || exit 11 +$EXTRACTRC $(find . -name '*.ui') >> rc.cpp || exit 12 +$EXTRACTRC $(find . -name '*.kcfg') >> rc.cpp +$XGETTEXT app/*.cpp kerfuffle/*.cpp part/*.cpp plugins/*/*.cpp rc.cpp -o $podir/ark.pot +rm -f rc.cpp diff --git a/ark/app/CMakeLists.txt b/ark/app/CMakeLists.txt new file mode 100644 index 00000000..f1ef01bc --- /dev/null +++ b/ark/app/CMakeLists.txt @@ -0,0 +1,50 @@ +add_subdirectory(icons) + +set(ark_SRCS + batchextract.cpp + main.cpp + mainwindow.cpp + ) + +# For Mac and Windows. +kde4_add_app_icon(ark_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/hi*-apps-ark.png") + +kde4_add_executable( ark ${ark_SRCS} ) + +target_link_libraries( ark kerfuffle ${KDE4_KFILE_LIBS} ${KDE4_KPARTS_LIBS} ) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/ark.desktop.cmake + ${CMAKE_CURRENT_BINARY_DIR}/ark.desktop +) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/ark_dndextract.desktop.cmake + ${CMAKE_CURRENT_BINARY_DIR}/ark_dndextract.desktop +) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/ark_servicemenu.desktop.cmake + ${CMAKE_CURRENT_BINARY_DIR}/ark_servicemenu.desktop +) + +install( TARGETS ark ${INSTALL_TARGETS_DEFAULT_ARGS} ) +install(FILES ark_addtoservicemenu.desktop ${CMAKE_CURRENT_BINARY_DIR}/ark_servicemenu.desktop DESTINATION ${SERVICES_INSTALL_DIR}/ServiceMenus) +install( PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/ark.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) +install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/ark.appdata.xml DESTINATION share/appdata ) +install( FILES arkui.rc DESTINATION ${DATA_INSTALL_DIR}/ark ) + +########### konqueror ark_extract_here plugin ############### +macro_optional_find_package( LibKonq ) +macro_log_feature( LIBKONQ_FOUND "LIBKONQ" "libkonq library" "kdebase" FALSE "" "Need to integrate in konqueror" ) + + +if (LIBKONQ_FOUND) + set(extracthere_SRCS batchextract.cpp extractHereDndPlugin.cpp) + kde4_add_plugin(extracthere WITH_PREFIX ${extracthere_SRCS}) + target_link_libraries(extracthere kerfuffle ${KDE4_KDECORE_LIBS} ${KDE4_KPARTS_LIBS} ${KDE4_KFILE_LIBS} ${LIBKONQ_LIBRARY} ) + include_directories(${LIBKONQ_INCLUDE_DIR}) + install( TARGETS extracthere DESTINATION ${PLUGIN_INSTALL_DIR} ) + install( FILES ${CMAKE_CURRENT_BINARY_DIR}/ark_dndextract.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +endif (LIBKONQ_FOUND) + diff --git a/ark/app/ark.appdata.xml b/ark/app/ark.appdata.xml new file mode 100644 index 00000000..9de32805 --- /dev/null +++ b/ark/app/ark.appdata.xml @@ -0,0 +1,226 @@ + + + ark.desktop + CC0-1.0 + GPL-2.0+ + Ark + آرك + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Ark + Арк + Ark + Арк + Ark + Ark + Ark + Ark + xxArkxx + Ark + 檔案壓縮_Ark + Archiving Tool + أداة أرشفة + Работа с архиви + Eina d'arxivament + Archivační nástroj + Arkiveringsværktøj + Archivprogramm + Archiving Tool + Herramienta de archivado + Arhiivide haldamise rakendus + Pakkausohjelma + Outil d'archivage + Fájltömörítő + Strumento di archiviazione + 압축 도구 + Archyvavimo įrankis + Arkiveringsverktøy + Archievwarktüüch + Archiefgereedschap + Narzędzie do archiwizowania + Ferramenta de Arquivo + Ferramenta de arquivamento + Archivačný nástroj + Orodje za ravnanje z arhivi + Алатка за архивирање + Alatka za arhiviranje + Алатка за архивирање + Alatka za arhiviranje + Arkiveringsverktyg + Arşivleme Aracı + Інструмент роботи з архівами + xxArchiving Toolxx + 压缩工具 + 壓縮工具 + +

+ Ark is a graphical file compression/decompression utility with support for multiple formats, + including tar, gzip, bzip2, rar and zip, as well as CD-ROM images. + Ark can be used to browse, extract, create, and modify archives. +

+

آرك أداة رسوميّة لضغط وفكّ ضغط الملفات مع دعم لصيغ متعدّدة، منها tar، وgzip، وbzip2، وrar، وzip، إضافة إلى صور الأقراص الضوئيّة. يمكن استخدام آرك لتصفح، واستخراج، وإنشاء، وتعديل الأرشيفات.

+

Ark е графичен инструмент за архивиране/разархивиране, поддържащ множество формати, включително tar, gzip, bzip2, rar и zip, както и образи на CD-ROM. Ark може да се използва за разглеждане, извличане, създаване и променяне на архиви.

+

L'Ark és una utilitat gràfica de compressió/descompressió de fitxers que implementa múltiples formats, incloent tar, gzip, bzip2, rar i zip, i també imatges de CD-ROM. L'Ark es pot utilitzar per explorar, extreure, crear, i modificar arxiua.

+

Ark er et grafisk værktøj til komprimering/dekomprimering, med understøttelse for flere formater, derunder tar, gzip, bzip2, rar og zip, såvel som cd-rom-imagefiler. Ark kan bruges til at gennemse, udtrække, oprette og redigere.

+

Ark ist ein grafisches Dienstprogramm zum Packen/Entpacken von Dateien mit Unterstützung für mehrere Formate wie tar, gzip, bzip2, rar, zip und auch CD-Abbilder. Mit Ark können Sie können Sie Archive durchsehen, entpacken, erstellen und bearbeiten.

+

Ark is a graphical file compression/decompression utility with support for multiple formats, including tar, gzip, bzip2, rar and zip, as well as CD-ROM images. Ark can be used to browse, extract, create, and modify archives.

+

Ark es una utilidad gráfica de compresión y descompresión de archivos que permite usar diversos formatos, como tar, gzip, bzip2, rar y zip, así como imágenes de CD-ROM. Ark se puede usar para explorar, extraer, crear y modificar archivos comprimidos.

+

Ark on graafiline failide tihendamise ehk kokkupakkimise ja nende lahtipakkimise tööriist, mis toetab väga paljusid vorminguid, sealhulgas tar, gzip, bzip2, rar and zip, samuti CD-ROM-i tõmmised. Arki abil saab arhiivifaile sirvida, lahti pakkida, luua ja muuta.

+

Ark on graafinen tiedostojen pakkaus-/purkuohjelma, joka tukee useita tiedostomuotoja kuten tar, gzip, bzip2, rar and zip sekä myös CD-ROM-levykuvia. Arkilla voi selata, purkaa, luoda ja muuttaa paketteja.

+

Ark est un utilitaire graphique de compression/décompression de fichier prenant en charge de multiples formats, notamment tar, gzip, bzip2, rar et zip, ainsi que les images de CD-ROM. Ark peut être utilisé pour parcourir, extraire, créer et modifier des archives.

+

Az Ark egy grafikus fájltömörítő és kibontó segédprogram többféle formátum támogatásával, beleértve a tar, gzip, bzip2, rar és zip formátumokat, valamint a CD-ROM képfájlokat. Az Ark használható archívumok böngészéséhez, kibontásához, létrehozásához és módosításához.

+

Ark è uno strumento grafico per la compressione/decompressione dei file che supporta molti formati, tra i quali tar, gzip, bzip2, rar e zip, così come le immagini dei CD-ROM. Ark può essere utilizzato per sfogliare, estrarre, creare e modificare archivi.

+

Ark는 그래픽 압축 파일 관리 도구이며, tar, gzip, bzip2, rar, zip, CD-ROM 이미지를 포함한 여러 파일 형식을 지원합니다. Ark를 사용하여 압축 파일을 열고, 생성하고, 수정하고, 압축을 풀 수 있습니다.

+

Ark er et verktøy med grafisk brukerflate, for komprimering/dekomprimering, med støtte for mange formater, deriblant tar, gzip, rar og zip, samt CD-ROM-bilder. Ark kan brukes til å bla i, pakke opp, opprette og endre arkiver.

+

Ark is en graafsch Komprimeer- un Dekomprimeerwarktüüch, dat en Barg Formaten ünnerstütten deit. Dor sünd tar, gzip, bzip2, rar un zip bi, un ok CD-ROM-Afbiller. Mit Ark kannst Du Archiven dörkieken, opstellen un ännern un Dateien dor ruttrecken.

+

Ark is een grafisch hulpmiddel om bestanden te comprimeren/uit te pakken met ondersteuning van meerdere formaten, inclusief tar, gzip, bzip2, rar en zip, evenals cd-ROM images. Ark kan gebruikt worden om door archieven te bladeren, deze uit te pakken, te maken en te wijzigen.

+

Ark jest graficznym narzędziem do pakowania/rozpakowywania plików wraz z obsługą różnych formatów, włączając w to tar, gzip, bzip2, rar oraz zip, a także obrazy CD-ROM. Programu Ark można używać do przeglądania, tworzenia, zmieniania oraz wydobywania z archiwów

+

O Ark é um utilitário de compressão/descompressão com o suporte para diversos formatos, incluindo o 'tar', 'gzip', 'bzip2', 'rar' e 'zip', assim como imagens de CD-ROM. O Ark pode ser usado para navegar, extrair, criar e modificar esses pacotes.

+

O Ark é um utilitário de compressão/descompressão com o suporte a diversos formatos, incluindo tar, gzip, bzip2, rar e zip, assim como imagens de CD-ROM. O Ark pode ser usado para navegar, extrair, criar e modificar esses arquivos.

+

Ark je grafický nástroj na kompresiu a dekompresiu súborov s podporou pre mnoho formátov, vrátane tar, gzip bzip2, rar a zip, ako ako CD-ROM obrazy. Ark sa dá použiť na prehliadanie, rozbaľovanie vytváranie a úpravu archívov.

+

Ark je grafično orodje za stiskanje/razširjanje datotek, ki podpira večje število arhivov (med podprtimi so tudi tar, gzip, bzip2, rar in zip) kot tudi odtise CD-ROM. Ark lahko uporabite za brskanje po, razširjanje, ustvarjanje in spreminjanje arhivov.

+

Арк је графичка алатка за компресовање и декомпресовање фајлова. Подржава више формата, међу њима и: тар, гзип, бзип2, РАР, ЗИП, као и ЦД одразе. Може се користити за прегледање, распакивање, стварање и мењање архива.

+

Ark je grafička alatka za kompresovanje i dekompresovanje fajlova. Podržava više formata, među njima i: tar, gzip, bzip2, RAR, ZIP, kao i CD odraze. Može se koristiti za pregledanje, raspakivanje, stvaranje i menjanje arhiva.

+

Арк је графичка алатка за компресовање и декомпресовање фајлова. Подржава више формата, међу њима и: тар, гзип, бзип2, РАР, ЗИП, као и ЦД одразе. Може се користити за прегледање, распакивање, стварање и мењање архива.

+

Ark je grafička alatka za kompresovanje i dekompresovanje fajlova. Podržava više formata, među njima i: tar, gzip, bzip2, RAR, ZIP, kao i CD odraze. Može se koristiti za pregledanje, raspakivanje, stvaranje i menjanje arhiva.

+

Ark är ett grafiskt verktyg för komprimering och uppackning av filer med stöd för flera format, inklusive tar, gzip, bzip2, rar och zip, samt cd-rom avbilder. Ark kan användas för att bläddra i, packa upp, skapa och ändra arkiv.

+

Ark; tar, gzip, bzip2, rar ve zip dosyalarının yanında CD-ROM kalıplarını da destekleyen grafiksel bir dosya sıkıştırma/açma yardımcısıdır. Ark arşivlerde gezinmek, arşiv ayıklamak, oluşturmak ve değiştirmek için kullanılabilir.

+

Ark — програма з графічним інтерфейсом, призначена для стискання даних у архіви та видобування даних з архівів. Передбачено підтримку декількох форматів архівів, зокрема tar, gzip, bzip2, rar та zip, а також образів дисків. Ark можна використовувати для перегляду, видобування, створення та внесення змін до архівів.

+

xxArk is a graphical file compression/decompression utility with support for multiple formats, including tar, gzip, bzip2, rar and zip, as well as CD-ROM images. Ark can be used to browse, extract, create, and modify archives.xx

+

Ark 是图形文件压缩和解压工具,支持多种格式,包括 tar, gzip, bzip2, rar and zip 以及 CD-ROM 镜像。Ark 可以用来浏览、解压、创建和修改压缩包。

+

Ark 是一套圖形介面的檔案壓縮/解壓縮工具,支援多種格式,包括 tar, gzip, bzip2, rar 與 zip 等,還有 CD-ROM 映像檔等。Ark 有瀏覽、解開、建立與變更壓縮檔的功能。

+

Features:

+

المزايا

+

Функции:

+

Característiques:

+

Vlastnosti:

+

Funktioner:

+

Leistungsmerkmale:

+

Features:

+

Funcionalidades:

+

Omadused:

+

Ominaisuudet:

+

Fonctionnalités :

+

Szolgáltatások:

+

Funzionalità:

+

기능:

+

Galimybės:

+

Egenskaper:

+

Markmalen:

+

Mogelijkheden:

+

Cechy:

+

Características:

+

Funcionalidades:

+

Funkcie:

+

Zmožnosti:

+

Могућности:

+

Mogućnosti:

+

Могућности:

+

Mogućnosti:

+

Funktioner:

+

Özellikler:

+

Можливості:

+

xxFeatures:xx

+

功能:

+

特色:

+
    +
  • Several formats supported: gzip, bzip2, zip, rar, 7z and more
  • +
  • دعم صيغ متعدّدة: مثل gzip، و bzip2، و zip، و rar، و 7z وغيرها
  • +
  • Поддържани са много формати: gzip, bzip2, zip, rar, 7z и други
  • +
  • Diversos formats implementats: gzip, bzip2, zip, rar, 7z i més
  • +
  • Je podporováno několik formátů: gzip, bzip2, zip, rar, 7z a další
  • +
  • Flere understøttede formater: gzip, bzip2, zip, rar, 7z og flere
  • +
  • Mehrere unterstützte Formate wie gzip, bzip2, zip, rar, 7z und mehr
  • +
  • Several formats supported: gzip, bzip2, zip, rar, 7z and more
  • +
  • Permite el uso de diversos formatos: gzip, bzip2, zip, rar, 7z y más
  • +
  • Paljude vormingute toetus: gzip, bzip2, zip, rar, 7z ja veel paljud.
  • +
  • Tukee useita tiedostomuotoja: gzip, bzip2, zip, rar, 7z ja monia muita
  • +
  • Plusieurs formats sont pris en charge : gzip, bzip2, zip, rar, 7z et d'autres
  • +
  • Számos formátum támogatott: gzip, bzip2, zip, rar, 7z és továbbiak
  • +
  • Diversi formati supportati: gzip, bzip2, zip, rar, 7z e altri
  • +
  • 다양한 형식 지원: gzip, bzip2, zip, rar, 7z 등
  • +
  • Mange formater støttet: gzip, bzip2, zip, rar, 7z og mer
  • +
  • En Reeg Fomaten warrt ünnerstütt: gzip, bzip2, zip, rar, 7z un anner
  • +
  • Ondersteuning voor een aantal formaten: gzip, bzip2, zip, rar, 7z en meer
  • +
  • Obsługa kilku formatów: gzip, bzip2, zip, rar, 7z oraz więcej
  • +
  • Diversos formatos suportados: gzip, bzip2, zip, rar, 7z, entre outros
  • +
  • Diversos formatos suportados: gzip, bzip2, zip, rar, 7z, entre outros
  • +
  • Niektoré z podporovaných formátov: gzip, bzip2, zip, rar, 7z a viac
  • +
  • Številne podprte vrste arhivov: gzip, bzip2, zip, rar, 7z in več
  • +
  • Више подржаних формата: гзип, бзип2, ЗИП, РАР, 7зип, итд.
  • +
  • Više podržanih formata: gzip, bzip2, ZIP, RAR, 7zip, itd.
  • +
  • Више подржаних формата: гзип, бзип2, ЗИП, РАР, 7зип, итд.
  • +
  • Više podržanih formata: gzip, bzip2, ZIP, RAR, 7zip, itd.
  • +
  • En mängd format stöds: gzip, bzip2, zip, rar, 7z med flera
  • +
  • Çeşitli biçimler destekleniyor: gzip, bzip2, zip, rar, 7z ve dahası
  • +
  • Підтримка декількох форматів: gzip, bzip2, zip, rar, 7z тощо
  • +
  • xxSeveral formats supported: gzip, bzip2, zip, rar, 7z and morexx
  • +
  • 支持多种格式:gzip, bzip2, zip, rar, 7z 等
  • +
  • 支援格式:gzip, bzip2, zip, rar, 7z 等等
  • +
  • Preview file contents without extracting files
  • +
  • استعراض محتوى الملف دون استخراج الملفات منه
  • +
  • Преглеждане съдържанието на файла, без извличане на файловете
  • +
  • Vista prèvia del contingut de fitxers sense extreure'ls
  • +
  • Náhled obsahu souborů bez jejich rozbalení
  • +
  • Forhåndsvis filindhold uden at udtrække filer
  • +
  • Vorschau der Dateiinhalte ohne das Entpacken der Dateien
  • +
  • Preview file contents without extracting files
  • +
  • Vista previa del contenido de archivos sin extraerlos
  • +
  • Failide sisu näitamine ilma neid lahti pakkimata.
  • +
  • Tiedoston sisällön esikatselu purkamatta tiedostoa
  • +
  • Prévisualiser le contenu des fichiers sans les extraire
  • +
  • Fájltartalom előnézete a fájlok kibontása nélkül
  • +
  • Anteprima dei file senza estrarre i file
  • +
  • 압축 풀지 않고 파일 내용 미리 보기
  • +
  • Forhåndsvis innhold uten å pakke ut filer
  • +
  • Datein ahn Ruttrecken vörweg ankieken
  • +
  • Voorvertoning van inhoud van bestand zonder bestanden uit te pakken
  • +
  • Podejrzyj zawartość pliku bez jego wypakowywania
  • +
  • Antevisão do conteúdo dos ficheiros sem os extrair
  • +
  • Visualização do conteúdo dos arquivos sem precisar extraí-los
  • +
  • Náhľad obsahu súboru bez rozbalenia súborov
  • +
  • Predoglejte vsebino datoteke brez razširjanja
  • +
  • Преглед садржаја фајлова без распакивања.
  • +
  • Pregled sadržaja fajlova bez raspakivanja.
  • +
  • Преглед садржаја фајлова без распакивања.
  • +
  • Pregled sadržaja fajlova bez raspakivanja.
  • +
  • Förhandsgranska filinnehåll utan att packa upp filer
  • +
  • Dosyaları ayıklamadan dosya içeriği önizlemesi
  • +
  • Попередній перегляд вмісту файлів без розпаковування
  • +
  • xxPreview file contents without extracting filesxx
  • +
  • 不解压就预览文件
  • +
  • 不需解壓縮即可預覽檔案內容
  • +
+
+ + + http://kde.org/images/screenshots/ark.png + + + http://kde.org/applications/utilities/ark/ + https://bugs.kde.org/enter_bug.cgi?format=guided&product=ark + http://docs.kde.org/stable/en/kdeutils/ark/index.html + KDE + + ark + +
diff --git a/ark/app/ark.desktop.cmake b/ark/app/ark.desktop.cmake new file mode 100755 index 00000000..9f0daeaf --- /dev/null +++ b/ark/app/ark.desktop.cmake @@ -0,0 +1,158 @@ +[Desktop Entry] +MimeType=@SUPPORTED_ARK_MIMETYPES@ +GenericName=Archiving Tool +GenericName[af]=Argiveer Program +GenericName[ar]=أداة أرشفة +GenericName[ast]=Ferramienta p'archivar +GenericName[bg]=Работа с архиви +GenericName[br]=Ostilh merañ an Dielloù +GenericName[bs]=Alatka za arhiviranje +GenericName[ca]=Eina d'arxivament +GenericName[ca@valencia]=Eina d'arxivament +GenericName[cs]=Archivační nástroj +GenericName[cy]=Erfyn Archifo +GenericName[da]=Arkiveringsværktøj +GenericName[de]=Archivprogramm +GenericName[el]=Εργαλείο αρχειοθέτησης +GenericName[en_GB]=Archiving Tool +GenericName[eo]=Arkivilo +GenericName[es]=Archivador +GenericName[et]=Arhiivide haldamise rakendus +GenericName[eu]=Artxibatzeko tresna +GenericName[fa]=ابزار بایگانی +GenericName[fi]=Pakkausohjelma +GenericName[fr]=Outil d'archivage +GenericName[ga]=Uirlis Chartlannaithe +GenericName[gl]=Utilidade de arquivo +GenericName[he]=כלי לניהול ארכיונים +GenericName[hne]=अभिलेखन औजार +GenericName[hr]=Alat za arhiviranje +GenericName[hu]=Fájltömörítő +GenericName[ia]=Instrumento per archivar +GenericName[id]=Perkakas Pengarsip +GenericName[is]=Vinna með safnskrár +GenericName[it]=Strumento di archiviazione +GenericName[ja]=アーカイブツール +GenericName[kk]=Архивтеу құралы +GenericName[km]=ឧបករណ៍​ប័ណ្ណសារ +GenericName[ko]=압축 도구 +GenericName[lt]=Archyvavimo priemonė +GenericName[lv]=Arhivēšanas rīks +GenericName[mk]=Алатка за архивирање +GenericName[mr]=संग्रह साधन +GenericName[ms]=Alatan Pengarkiban +GenericName[nb]=Arkiveringsverktøy +GenericName[nds]=Archievwarktüüch +GenericName[ne]=सङ्ग्रहण उपकरण +GenericName[nl]=Archiefgereedschap +GenericName[nn]=Arkiveringsverktøy +GenericName[pa]=ਅਕਾਇਵਿੰਗ ਟੂਲ +GenericName[pl]=Narzędzie do archiwizacji +GenericName[pt]=Ferramenta de Armazenamento +GenericName[pt_BR]=Ferramenta de Arquivamento +GenericName[ro]=Utilitar de arhivare +GenericName[ru]=Архиватор +GenericName[sk]=Archivačný nástroj +GenericName[sl]=Orodje za ravnanje z arhivi +GenericName[sq]=Mjeti Arkivues +GenericName[sr]=Алатка за архивирање +GenericName[sr@ijekavian]=Алатка за архивирање +GenericName[sr@ijekavianlatin]=Alatka za arhiviranje +GenericName[sr@latin]=Alatka za arhiviranje +GenericName[sv]=Arkiveringsverktyg +GenericName[ta]=காப்பக கருவி +GenericName[tg]=Асбобҳои Бойгонӣ +GenericName[th]=เครื่องมือจัดการแฟ้มจัดเก็บ +GenericName[tr]=Arşivleme Aracı +GenericName[ug]=ئارخىپ قورالى +GenericName[uk]=Засіб роботи з архівами +GenericName[uz]=Arxivlash vositasi +GenericName[uz@cyrillic]=Архивлаш воситаси +GenericName[vi]=Công Cụ Nén +GenericName[wa]=Usteye d' årtchivaedje +GenericName[xh]=Isixhobo Sokuphatha i Archive +GenericName[x-test]=xxArchiving Toolxx +GenericName[zh_CN]=压缩归档工具 +GenericName[zh_TW]=壓縮工具 +Name=Ark +Name[af]=Ark +Name[ar]=أرك +Name[ast]=Ark +Name[bg]=Ark +Name[br]=Ark +Name[bs]=Ark +Name[ca]=Ark +Name[ca@valencia]=Ark +Name[cs]=Ark +Name[cy]=Ark +Name[da]=Ark +Name[de]=Ark +Name[el]=Ark +Name[en_GB]=Ark +Name[eo]=Arko +Name[es]=Ark +Name[et]=Ark +Name[eu]=Ark +Name[fi]=Ark +Name[fr]=Ark +Name[ga]=Ark +Name[gl]=Ark +Name[he]=Ark +Name[hne]=आर्क +Name[hr]=Ark +Name[hu]=Ark +Name[ia]=Ark +Name[id]=Ark +Name[is]=Ark +Name[it]=Ark +Name[ja]=Ark +Name[kk]=Ark +Name[km]=Ark +Name[ko]=Ark +Name[lt]=Ark +Name[lv]=Ark +Name[mk]=Ark +Name[mr]=आर्क +Name[ms]=Ark +Name[nb]=Ark +Name[nds]=Ark +Name[ne]=आर्क +Name[nl]=Ark +Name[nn]=Ark +Name[pa]=ਆਕ +Name[pl]=Ark +Name[pt]=Ark +Name[pt_BR]=Ark +Name[ro]=Ark +Name[ru]=Ark +Name[sk]=Ark +Name[sl]=Ark +Name[sq]=Ark +Name[sr]=Арк +Name[sr@ijekavian]=Арк +Name[sr@ijekavianlatin]=Ark +Name[sr@latin]=Ark +Name[sv]=Ark +Name[ta]=ஆர்க் +Name[tg]=Ark +Name[th]=อาร์ก +Name[tr]=Ark +Name[ug]=Ark +Name[uk]=Ark +Name[uz]=Ark +Name[uz@cyrillic]=Ark +Name[vi]=Ark +Name[wa]=Ark +Name[xh]=Ark +Name[x-test]=xxArkxx +Name[zh_CN]=Ark +Name[zh_TW]=Ark +Exec=ark -caption %c %U +Icon=ark +X-DocPath=ark/index.html +Type=Application +Terminal=false +X-DBUS-StartupType=Unique +X-KDE-HasTempFileOption=true +Categories=Qt;KDE;Utility;Archiving;Compression;X-KDE-Utilities-File; +InitialPreference=3 diff --git a/ark/app/ark_addtoservicemenu.desktop b/ark/app/ark_addtoservicemenu.desktop new file mode 100644 index 00000000..73cb805d --- /dev/null +++ b/ark/app/ark_addtoservicemenu.desktop @@ -0,0 +1,351 @@ +[Desktop Entry] +Type=Service +ServiceTypes=KonqPopupMenu/Plugin +MimeType=all/all; +Actions=compressHere;compressAsZip;compressAsRar;compressAsTar;compressTo; +X-KDE-Submenu=Compress +X-KDE-Submenu[ar]=اضغط +X-KDE-Submenu[ast]=Comprimir +X-KDE-Submenu[bg]=Компресиране +X-KDE-Submenu[bs]=Kompresuj +X-KDE-Submenu[ca]=Compressió +X-KDE-Submenu[ca@valencia]=Compressió +X-KDE-Submenu[cs]=Zkomprimovat +X-KDE-Submenu[da]=Komprimér +X-KDE-Submenu[de]=Komprimieren +X-KDE-Submenu[el]=Συμπίεση +X-KDE-Submenu[en_GB]=Compress +X-KDE-Submenu[es]=Comprimir +X-KDE-Submenu[et]=Paki +X-KDE-Submenu[eu]=Konprimatu +X-KDE-Submenu[fi]=Pakkaa +X-KDE-Submenu[fr]=Compresser +X-KDE-Submenu[ga]=Comhbhrúigh +X-KDE-Submenu[gl]=Comprimir +X-KDE-Submenu[hne]=संपीडित करव +X-KDE-Submenu[hr]=Zapakiraj +X-KDE-Submenu[hu]=Tömörítés +X-KDE-Submenu[ia]=Comprime +X-KDE-Submenu[id]=Kompres +X-KDE-Submenu[it]=Comprimi +X-KDE-Submenu[ja]=圧縮 +X-KDE-Submenu[kk]=Сығу +X-KDE-Submenu[km]=បង្ហាប់ +X-KDE-Submenu[ko]=압축하기 +X-KDE-Submenu[lt]=Suspausti +X-KDE-Submenu[lv]=Saspiest +X-KDE-Submenu[mr]=संक्षिप्त करा +X-KDE-Submenu[nb]=Komprimer +X-KDE-Submenu[nds]=Komprimeren +X-KDE-Submenu[nl]=Comprimeren +X-KDE-Submenu[nn]=Komprimer +X-KDE-Submenu[pa]=ਕੰਪਰੈੱਸ +X-KDE-Submenu[pl]=Kompresuj +X-KDE-Submenu[pt]=Comprimir +X-KDE-Submenu[pt_BR]=Compactar +X-KDE-Submenu[ro]=Comprimă +X-KDE-Submenu[ru]=Упаковать +X-KDE-Submenu[sk]=Komprimovať +X-KDE-Submenu[sl]=Stisni +X-KDE-Submenu[sq]=Ngjish +X-KDE-Submenu[sr]=Компресуј +X-KDE-Submenu[sr@ijekavian]=Компресуј +X-KDE-Submenu[sr@ijekavianlatin]=Kompresuj +X-KDE-Submenu[sr@latin]=Kompresuj +X-KDE-Submenu[sv]=Komprimera +X-KDE-Submenu[th]=บีบข้อมูล +X-KDE-Submenu[tr]=Sıkıştır +X-KDE-Submenu[ug]=پرېس +X-KDE-Submenu[uk]=Стиснути +X-KDE-Submenu[wa]=Rastrinde +X-KDE-Submenu[x-test]=xxCompressxx +X-KDE-Submenu[zh_CN]=压缩 +X-KDE-Submenu[zh_TW]=壓縮 +X-KDE-StartupNotify=false +X-KDE-Priority=TopLevel + +[Desktop Action compressHere] +Name=Here +Name[ar]=هنا +Name[ast]=Equí +Name[bg]=Тук +Name[bs]=ovdje +Name[ca]=Aquí +Name[ca@valencia]=Ací +Name[cs]=Sem +Name[da]=Her +Name[de]=Hier +Name[el]=Εδώ +Name[en_GB]=Here +Name[es]=Aquí +Name[et]=Siia +Name[eu]=Hemen +Name[fi]=Tähän +Name[fr]=Ici +Name[ga]=Anseo +Name[gl]=Aquí +Name[hr]=Ovdje +Name[hu]=Ide +Name[ia]=Hic +Name[id]=Di Sini +Name[is]=Hér +Name[it]=Qui +Name[ja]=ここに +Name[kk]=Осында +Name[km]=នៅ​ទីនេះ +Name[ko]=여기 +Name[lt]=Čia +Name[lv]=Šeit +Name[mr]=येथे +Name[nb]=Her +Name[nds]=Hier +Name[nl]=Hier +Name[nn]=Her +Name[pa]=ਇੱਥੇ +Name[pl]=Tutaj +Name[pt]=Aqui +Name[pt_BR]=Aqui +Name[ro]=Aici +Name[ru]=В эту папку +Name[sk]=Sem +Name[sl]=Sem +Name[sq]=Këtu +Name[sr]=овде +Name[sr@ijekavian]=овдје +Name[sr@ijekavianlatin]=ovdje +Name[sr@latin]=ovde +Name[sv]=Här +Name[th]=ไว้ที่นี่ +Name[tr]=Buraya +Name[ug]=بۇ جاي +Name[uk]=Сюди +Name[wa]=Cial +Name[x-test]=xxHerexx +Name[zh_CN]=这里 +Name[zh_TW]=這裡 +Icon=ark +Exec=ark --changetofirstpath --add --autofilename tar.gz %F + +[Desktop Action compressAsZip] +Name=As ZIP Archive +Name[ar]=ك أرشيف ZIP +Name[ast]=Como archivu comprimíu ZIP +Name[bg]=Като ZIP архив +Name[bs]=kao ZIP arhivu +Name[ca]=Com a arxiu ZIP +Name[ca@valencia]=Com a arxiu ZIP +Name[cs]=Jako archiv ZIP +Name[da]=Som ZIP-arkiv +Name[de]=Als ZIP-Archiv +Name[el]=Ως αρχειοθήκη ZIP +Name[en_GB]=As ZIP Archive +Name[es]=Como archivo comprimido ZIP +Name[et]=ZIP-arhiivina +Name[eu]=ZIP artxibo gisa +Name[fi]=ZIP-paketiksi +Name[fr]=Comme une archive « ZIP » +Name[ga]=Mar Chartlann ZIP +Name[gl]=Como arquivo ZIP +Name[hr]=Kao ZIP-arhiva +Name[hu]=ZIP archívumként +Name[ia]=Como archivo ZIP +Name[id]=Sebagai Arsip ZIP +Name[it]=Come archivio ZIP +Name[ja]=ZIP アーカイブに +Name[kk]=ZIP архиві қылып +Name[km]=ជា​ប័ណ្ណសារ ZIP +Name[ko]=ZIP 파일로 +Name[lt]=ZIP archyvas +Name[lv]=Kā ZIP arhīvu +Name[mr]=ZIP संग्रह प्रमाणे +Name[nb]=Som ZIP-arkiv +Name[nds]=As Zip-Archiev +Name[nl]=Als ZIP-archief +Name[nn]=Som ZIP-arkiv +Name[pa]=ਜ਼ਿਪ ਅਕਾਇਵ ਵਾਂਗ +Name[pl]=Jako archiwum ZIP +Name[pt]=Como Pacote ZIP +Name[pt_BR]=Como arquivo ZIP +Name[ro]=Ca arhivă ZIP +Name[ru]=Как архив ZIP +Name[sk]=Do ZIP archívu +Name[sl]=Kot arhiv ZIP +Name[sq]=Si Arkiv ZIP +Name[sr]=као ЗИП архиву +Name[sr@ijekavian]=као ЗИП архиву +Name[sr@ijekavianlatin]=kao ZIP arhivu +Name[sr@latin]=kao ZIP arhivu +Name[sv]=Som ZIP-arkiv +Name[th]=เป็นแฟ้มจัดเก็บบีบอัดแบบ ZIP +Name[tr]=ZIP Arşivi Olarak +Name[ug]=ZIP ئارخىپى +Name[uk]=Як архів ZIP +Name[wa]=Come årtchive ZIP +Name[x-test]=xxAs ZIP Archivexx +Name[zh_CN]=为 ZIP 归档 +Name[zh_TW]=成 ZIP 壓縮檔 +Icon=ark +Exec=ark --changetofirstpath --add --autofilename zip %F + +[Desktop Action compressAsRar] +Name=As RAR Archive +Name[ar]=كأرشيف RAR +Name[ast]=Como archivu comprimíu RAR +Name[bg]=Като RAR архив +Name[bs]=kao RAR arhivu +Name[ca]=Com a arxiu RAR +Name[ca@valencia]=Com a arxiu RAR +Name[cs]=Jako archiv RAR +Name[da]=Som RAR-arkiv +Name[de]=Als RAR-Archiv +Name[el]=Ως αρχειοθήκη RAR +Name[en_GB]=As RAR Archive +Name[es]=Como archivo comprimido RAR +Name[et]=RAR-arhiivina +Name[eu]=RAR artxibo gisa +Name[fi]=RAR-paketiksi +Name[fr]=Comme une archive « RAR » +Name[ga]=Mar Chartlann RAR +Name[gl]=Como arquivo RAR +Name[hr]=Kao RAR-arhiva +Name[hu]=RAR archívumként +Name[ia]=Como archivo RAR +Name[id]=Sebagai Arsip RAR +Name[it]=Come archivio RAR +Name[ja]=RAR アーカイブに +Name[kk]=RAR архиві қылып +Name[km]=​ជា​ប័ណ្ណសារ RAR +Name[ko]=RAR 파일로 +Name[lt]=RAR archyvas +Name[lv]=Kā RAR arhīvu +Name[mr]=RAR संग्रह प्रमाणे +Name[nb]=Som RAR-arkiv +Name[nds]=As RAR-Archiev +Name[nl]=Als RAR-archief +Name[nn]=Som RAR-arkiv +Name[pa]=RAR ਅਕਾਇਵ ਵਾਂਗ +Name[pl]=Jako archiwum RAR +Name[pt]=Como Pacote RAR +Name[pt_BR]=Como arquivo RAR +Name[ro]=Ca arhivă RAR +Name[ru]=Как архив RAR +Name[sk]=Do RAR archívu +Name[sl]=Kot arhiv RAR +Name[sq]=Si Arkiv RAR +Name[sr]=као РАР архиву +Name[sr@ijekavian]=као РАР архиву +Name[sr@ijekavianlatin]=kao RAR arhivu +Name[sr@latin]=kao RAR arhivu +Name[sv]=Som RAR-arkiv +Name[th]=เป็นแฟ้มจัดเก็บบีบอัดแบบ RAR +Name[tr]=RAR Arşivi Olarak +Name[ug]=RAR ئارخىپى +Name[uk]=Як архів RAR +Name[wa]=Come årtchive RAR +Name[x-test]=xxAs RAR Archivexx +Name[zh_CN]=为 RAR 归档 +Name[zh_TW]=成 RAR 壓縮檔 +Icon=ark +Exec=ark --changetofirstpath --add --autofilename rar %F + +[Desktop Action compressAsTar] +Name=As TAR.GZ Archive +Name[ar]=كأرشيف TAR.GZ +Name[bg]=Като TAR.GZ архив +Name[ca]=Com a arxiu TAR.GZ +Name[cs]=Jako archiv TAR.GZ +Name[da]=Som TAR.GZ-arkiv +Name[de]=Als TAR.GZ-Archiv +Name[en_GB]=As TAR.GZ Archive +Name[es]=Como archivo comprimido TAR.GZ +Name[et]=TAR.GZ-arhiivina +Name[fi]=TAR.GZ-paketiksi +Name[fr]=Comme une archive « TAR.GZ » +Name[hu]=TAR.GZ archívumként +Name[ia]=Como archivo TAR.GZ +Name[it]=Come archivio TAR.GZ +Name[ja]=TAR.GZ アーカイブに +Name[ko]=TAR.GZ 파일로 +Name[nb]=Som TAR.GZ-arkiv +Name[nds]=As TAR.GZ-Archiev +Name[nl]=Als TAR.GZ-archief +Name[pa]=TAR.GZ ਅਕਾਇਵ ਵਜੋਂ +Name[pl]=Jako archiwum TAR.GZ +Name[pt]=Como Pacote TAR.GZ +Name[pt_BR]=Como arquivo TAR.GZ +Name[ru]=Как архив TAR.GZ +Name[sk]=Ako TAR.GZ archív +Name[sl]=Kot arhiv TAR.GZ +Name[sr]=као таргз архиву +Name[sr@ijekavian]=као таргз архиву +Name[sr@ijekavianlatin]=kao targz arhivu +Name[sr@latin]=kao targz arhivu +Name[sv]=Som TAR.GZ-arkiv +Name[tr]=TAR.GZ Arşivi Olarak +Name[uk]=Як архів TAR.GZ +Name[x-test]=xxAs TAR.GZ Archivexx +Name[zh_CN]=为 TAR.GZ 归档 +Name[zh_TW]=成 TAR.GZ 壓縮檔 +Icon=ark +Exec=ark --changetofirstpath --add --autofilename tar.gz %F + +[Desktop Action compressTo] +Name=Compress To... +Name[ar]=اضغط إلى... +Name[ast]=Comprimir en... +Name[bg]=Компресиране в... +Name[bs]=Kompresuj u... +Name[ca]=Comprimeix a... +Name[ca@valencia]=Comprimeix a... +Name[cs]=Zkomprimovat do... +Name[da]=Komprimér til... +Name[de]=Komprimieren nach ... +Name[el]=Συμπίεση σε... +Name[en_GB]=Compress To... +Name[es]=Comprimir en... +Name[et]=Paki asukohta... +Name[eu]=Konprimatu hona... +Name[fi]=Pakkaa… +Name[fr]=Compresser vers... +Name[ga]=Comhbhrúigh Go... +Name[gl]=Comprimir en... +Name[hr]=Zapakiraj u … +Name[hu]=Tömörítés ide… +Name[ia]=Comprime a... +Name[id]=Kompres ke... +Name[it]=Comprimi in... +Name[ja]=圧縮... +Name[kk]=Мыныған архивтеу,,, +Name[km]=បង្ហាប់​ទៅ​ជា​... +Name[ko]=다음으로 압축하기... +Name[lt]=Suspausti į... +Name[lv]=Saspiest uz... +Name[mr]=यावर संक्षिप्त करा... +Name[nb]=Komprimer til … +Name[nds]=Komprimeren as... +Name[nl]=Comprimeren naar... +Name[nn]=Komprimer til … +Name[pa]=...ਵਜੋਂ ਕੰਪਰੈੱਸ ਕਰੋ +Name[pl]=Kompresuj do... +Name[pt]=Comprimir Para... +Name[pt_BR]=Compactar para... +Name[ro]=Comprimă în... +Name[ru]=Упаковать в архив... +Name[sk]=Komprimovať do... +Name[sl]=Stisni v ... +Name[sq]=Ngjishe Tek... +Name[sr]=другде... +Name[sr@ijekavian]=другдје... +Name[sr@ijekavianlatin]=drugdje... +Name[sr@latin]=drugde... +Name[sv]=Komprimera till... +Name[th]=บีบข้อมูลไปยัง... +Name[tr]=Farklı Sıkıştır... +Name[ug]=… غا پرېسلاش +Name[uk]=Стиснути до... +Name[wa]=Rastrinde eyet rlomer... +Name[x-test]=xxCompress To...xx +Name[zh_CN]=压缩到... +Name[zh_TW]=壓縮為... +Icon=ark +Exec=ark --add --changetofirstpath --dialog %F diff --git a/ark/app/ark_dndextract.desktop.cmake b/ark/app/ark_dndextract.desktop.cmake new file mode 100644 index 00000000..f1ac26a4 --- /dev/null +++ b/ark/app/ark_dndextract.desktop.cmake @@ -0,0 +1,61 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=KonqDndPopupMenu/Plugin +X-KDE-Library=libextracthere +Name=Ark Extract Here +Name[ar]=أرك استخرج هنا +Name[ast]=Estrayer equí con Ark +Name[bg]=Извличане тук +Name[bs]=Raspakuj Arkom ovde +Name[ca]=Extracció de l'Ark aquí +Name[ca@valencia]=Extracció de l'Ark ací +Name[cs]=Rozbalit Arkem sem +Name[da]=Pak ud med Ark her +Name[de]=Ark – Hierher auspacken +Name[el]=Εξαγωγή Ark εδώ +Name[en_GB]=Ark Extract Here +Name[es]=Extraer aquí con Ark +Name[et]=Paki siia lahti +Name[eu]=Ark: atera hemen +Name[fi]=Ark – Pura tähän +Name[fr]=Extraire un fichier « Ark » ici +Name[ga]=Bain an chartlann Ark amach agus cuir é anseo +Name[gl]=Extraer aquí con Ark +Name[hr]=Ovdje Ark-otpakiraj +Name[hu]=Kibontás ide +Name[ia]=Ark Extrahe hic +Name[id]=Ark Ekstrak di Sini +Name[it]=Ark Estrai qui +Name[ja]=Ark ここに展開 +Name[kk]=Мынаған тарқату +Name[km]=ស្រង់ចេញ Ark នៅ​ទីនេះ +Name[ko]=Ark 여기에 풀기 +Name[lt]=Čia išpakuoti archyvą +Name[lv]=Ark atspiest šeit +Name[mr]=येथे आर्क पुर्ववत करा +Name[nb]=Pakk ut arkivet her med Ark +Name[nds]=Hier mit Ark utpacken +Name[nl]=Ark: hier uitpakken +Name[nn]=Pakk ut med Ark her +Name[pa]=ਅਕਾਇਵ ਇੱਥੇ ਖੋਲ੍ਹੋ +Name[pl]=Rozpakuj tutaj +Name[pt]=Extrair para Aqui +Name[pt_BR]=Extrair com o Ark aqui +Name[ro]=Ark extrage aici +Name[ru]=Распаковать в эту папку +Name[sk]=Ark - rozbaliť sem +Name[sl]=Razširi sem z Arkom +Name[sq]=Ark Ekstrakton Këtu +Name[sr]=Распакуј Арком овде +Name[sr@ijekavian]=Распакуј Арком овдје +Name[sr@ijekavianlatin]=Raspakuj Arkom ovdje +Name[sr@latin]=Raspakuj Arkom ovde +Name[sv]=Packa upp arkiv här +Name[th]=อาร์ก คลายแฟ้มไว้ที่นี่ +Name[tr]=Ark Arşivi Buraya Çıkart +Name[uk]=Видобути архів сюди з Ark +Name[wa]=Saetchî foû avou Ark cial +Name[x-test]=xxArk Extract Herexx +Name[zh_CN]=Ark 在此解压缩 +Name[zh_TW]=在此解壓縮 +MimeType=@SUPPORTED_ARK_MIMETYPES@ diff --git a/ark/app/ark_servicemenu.desktop.cmake b/ark/app/ark_servicemenu.desktop.cmake new file mode 100644 index 00000000..41772bdf --- /dev/null +++ b/ark/app/ark_servicemenu.desktop.cmake @@ -0,0 +1,238 @@ +[Desktop Entry] +Type=Service +ServiceTypes=KonqPopupMenu/Plugin +MimeType=@SUPPORTED_ARK_MIMETYPES@ +Actions=arkAutoExtractHere;arkExtractTo;arkExtractHere; +X-KDE-Priority=TopLevel +X-KDE-StartupNotify=false +#StartupNotify=false +X-KDE-Submenu=Extract +X-KDE-Submenu[ar]=استخرِج +X-KDE-Submenu[bg]=Извличане +X-KDE-Submenu[bs]=Raspakivanje +X-KDE-Submenu[ca]=Extreu +X-KDE-Submenu[ca@valencia]=Extrau +X-KDE-Submenu[cs]=Rozbalit +X-KDE-Submenu[da]=Pak ud +X-KDE-Submenu[de]=Entpacken +X-KDE-Submenu[el]=Εξαγωγή +X-KDE-Submenu[en_GB]=Extract +X-KDE-Submenu[es]=Extraer +X-KDE-Submenu[et]=Lahtipakkimine +X-KDE-Submenu[eu]=Atera +X-KDE-Submenu[fi]=Pura +X-KDE-Submenu[fr]=Extraire +X-KDE-Submenu[ga]=Bain amach +X-KDE-Submenu[gl]=Extraer +X-KDE-Submenu[hr]=Otpakiraj +X-KDE-Submenu[hu]=Kibontás +X-KDE-Submenu[ia]=Extrahe +X-KDE-Submenu[it]=Estrai +X-KDE-Submenu[ja]=展開 +X-KDE-Submenu[kk]=Тарқату +X-KDE-Submenu[km]=ស្រង់ចេញ​ +X-KDE-Submenu[ko]=압축 풀기 +X-KDE-Submenu[lt]=Išpakuoti +X-KDE-Submenu[mr]=पुर्ववत करा +X-KDE-Submenu[nb]=Pakk ut +X-KDE-Submenu[nds]=Utpacken +X-KDE-Submenu[nl]=Uitpakken +X-KDE-Submenu[pa]=ਇੱਥੇ ਖਿਲਾਰੋ +X-KDE-Submenu[pl]=Rozpakuj +X-KDE-Submenu[pt]=Extrair +X-KDE-Submenu[pt_BR]=Extrair +X-KDE-Submenu[ro]=Extrage +X-KDE-Submenu[ru]=Распаковать +X-KDE-Submenu[sk]=Rozbaliť +X-KDE-Submenu[sl]=Razširi +X-KDE-Submenu[sr]=Распакуј +X-KDE-Submenu[sr@ijekavian]=Распакуј +X-KDE-Submenu[sr@ijekavianlatin]=Raspakuj +X-KDE-Submenu[sr@latin]=Raspakuj +X-KDE-Submenu[sv]=Packa upp +X-KDE-Submenu[tr]=Çıkart +X-KDE-Submenu[ug]=ئايرىش +X-KDE-Submenu[uk]=Видобути +X-KDE-Submenu[x-test]=xxExtractxx +X-KDE-Submenu[zh_CN]=解压缩 +X-KDE-Submenu[zh_TW]=解開 + +[Desktop Action arkExtractHere] +Name=Extract Archive Here +Name[ar]=فك الأرشيف هنا +Name[ast]=Estrayer archivu comprimíu equí +Name[bg]=Извличане тук +Name[bs]=Raspakuj arhivu ovde +Name[ca]=Extreu l'arxiu aquí +Name[ca@valencia]=Extrau l'arxiu ací +Name[cs]=Rozbalit archiv sem +Name[da]=Udpak arkiv her +Name[de]=Archiv hierher auspacken +Name[el]=Εξαγωγή αρχειοθήκης εδώ +Name[en_GB]=Extract Archive Here +Name[es]=Extraer archivo comprimido aquí +Name[et]=Paki arhiiv siia lahti +Name[eu]=Atera artxiboa hemen +Name[fi]=Pura paketti tähän +Name[fr]=Extraire l'archive ici +Name[ga]=Bain an chartlann amach agus cuir é anseo +Name[gl]=Extraer o arquivo aquí +Name[hr]=Ovdje otpakiraj arhivu +Name[hu]=Kibontás ide +Name[ia]=Extrahe archivo hic +Name[id]=Ekstrak Arsip di Sini +Name[it]=Estrai l'archivio qui +Name[ja]=アーカイブをここに展開 +Name[kk]=Архивті мынаған тарқату +Name[km]=ស្រង់ចេញ​ប័ណ្ណសារ​នៅ​ទីនេះ +Name[ko]=여기에 압축 풀기 +Name[lt]=Čia išpakuoti archyvą +Name[lv]=Atspiest arhīvu šeit +Name[mr]=येथे संग्रह पुर्ववत करा +Name[nb]=Pakk ut arkivet her +Name[nds]=Archiev hier utpacken +Name[nl]=Archief hier uitpakken +Name[nn]=Pakk ut arkivet her +Name[pa]=ਅਕਾਇਵ ਇੱਥੇ ਖੋਲ੍ਹੋ +Name[pl]=Rozpakuj archiwum tutaj +Name[pt]=Extrair o Pacote para Aqui +Name[pt_BR]=Extrair arquivo aqui +Name[ro]=Extrage arhiva aici +Name[ru]=В эту папку +Name[sk]=Rozbaliť archív sem +Name[sl]=Razširi arhiv sem +Name[sq]=Ekstrakto Arkivin Këtu +Name[sr]=Распакуј архиву овде +Name[sr@ijekavian]=Распакуј архиву овдје +Name[sr@ijekavianlatin]=Raspakuj arhivu ovdje +Name[sr@latin]=Raspakuj arhivu ovde +Name[sv]=Packa upp arkiv här +Name[th]=คลายแฟ้มจัดเก็บไว้ที่นี่ +Name[tr]=Arşivi Buraya Çıkart +Name[uk]=Видобути архів сюди +Name[wa]=Saetchî l' årtchive foû cial +Name[x-test]=xxExtract Archive Herexx +Name[zh_CN]=在此解压缩归档 +Name[zh_TW]=在此解壓縮 +Icon=ark +Exec=ark --batch --autodestination %F + +[Desktop Action arkExtractTo] +Name=Extract Archive To... +Name[ar]=فك الأرشيف إلى... +Name[ast]=Estrayer archivu comprimíu en... +Name[bg]=Извличане в... +Name[bs]=Raspakuj arhivu u... +Name[ca]=Extreu l'arxiu a... +Name[ca@valencia]=Extrau l'arxiu a... +Name[cs]=Rozbalit archiv do... +Name[da]=Udpak arkiv til... +Name[de]=Archiv auspacken nach ... +Name[el]=Εξαγωγή αρχειοθήκης σε... +Name[en_GB]=Extract Archive To... +Name[es]=Extraer archivo comprimido en... +Name[et]=Paki arhiiv lahti... +Name[eu]=Atera artxiboa hona... +Name[fi]=Pura paketti… +Name[fr]=Extraire l'archive vers... +Name[ga]=Bain an chartlann amach go... +Name[gl]=Extraer o arquivo en... +Name[hr]=Otpakiraj arhivu u … +Name[hu]=Kibontás ide… +Name[ia]=Extrahe archivo in... +Name[id]=Ekstrak Arsip ke... +Name[it]=Estrai l'archivio in... +Name[ja]=アーカイブを展開... +Name[kk]=Мынаған тарқату... +Name[km]=ស្រង់ចេញ​ប័ណ្ណសារ​ទៅ... +Name[ko]=다음 경로에 압축 풀기... +Name[lt]=Išpakuoti archyvą į... +Name[lv]=Atspiest arhīvu uz... +Name[mr]=यावर संग्रह पुर्ववत करा... +Name[nb]=Pakk ut arkivet til … +Name[nds]=Archiev utpacken as... +Name[nl]=Archief uitpakken naar... +Name[nn]=Pakk ut arkivet til … +Name[pa]=...ਵਜੋਂ ਅਕਾਇਵ ਐਕਸਟਰੈਕਟ +Name[pl]=Rozpakuj archiwum do... +Name[pt]=Extrair o Pacote Para... +Name[pt_BR]=Extrair arquivo para... +Name[ro]=Extrage arhiva în... +Name[ru]=Указать путь... +Name[sk]=Rozbaliť archív do... +Name[sl]=Razširi arhiv v ... +Name[sq]=Ekstrakto Arkivin Tek... +Name[sr]=Распакуј архиву у... +Name[sr@ijekavian]=Распакуј архиву у... +Name[sr@ijekavianlatin]=Raspakuj arhivu u... +Name[sr@latin]=Raspakuj arhivu u... +Name[sv]=Packa upp arkiv i... +Name[th]=คลายแฟ้มจัดเก็บไปยัง... +Name[tr]=Arşivi Şuraya Çıkart... +Name[uk]=Видобути архів до... +Name[wa]=Saetchî l' årtchive foû eyet l' rilomer... +Name[x-test]=xxExtract Archive To...xx +Name[zh_CN]=解压缩归档到... +Name[zh_TW]=解壓縮到... +Icon=ark +Exec=ark --batch --autodestination --dialog %F + +[Desktop Action arkAutoExtractHere] +Name=Extract Archive Here, Autodetect Subfolder +Name[ar]=فك الأرشيف هنا ، تعرف تلقائي للمجلدات الفرعية +Name[ast]=Estrayer archivu comprimíu equí, autodeteutar subcarpeta +Name[bg]=Извличане тук, автоматична подпапка +Name[bs]=Raspakuj arhivu ovdje, pogodi poddirektorij +Name[ca]=Extreu l'arxiu aquí, detecta automàticament la subcarpeta +Name[ca@valencia]=Extrau l'arxiu ací, detecta automàticament la subcarpeta +Name[cs]=Rozbalit archiv sem, automaticky detekovat podsložku +Name[da]=Udpak arkiv her og find automatisk undermappe +Name[de]=Archiv hierher auspacken, Unterordner selbständig ermitteln +Name[el]=Εξαγωγή αρχειοθήκης εδώ, αυτόματη επιλογή υποφακέλου +Name[en_GB]=Extract Archive Here, Autodetect Subfolder +Name[es]=Extraer archivo comprimido aquí, autodetectar subcarpeta +Name[et]=Paki arhiiv siia lahti, tuvasta automaatselt alamkataloog +Name[eu]=Atera artxiboa hona, automatikoki detektatu azpikarpeta +Name[fi]=Pura paketti tähän, tunnista alikansio automaattisesti +Name[fr]=Extraire l'archive ici, auto-détecter les sous-dossiers +Name[ga]=Bain an chartlann amach agus cuir é anseo, braith fofhillteán go huathoibríoch +Name[gl]=Extraer o arquivo aquí, detectar o subcartafol +Name[hr]=Ovdje otpakiraj arhivu i automatski prepoznaj podmapu +Name[hu]=Kibontás ide (automatikus almappalétrehozás) +Name[ia]=Extrahe archivo hic, auto-releva subdossier +Name[id]=Ekstrak Arsip di Sini, Deteksi Otomatis Subfolder +Name[it]=Estrai l'archivio qui, autorileva la sottocartella +Name[ja]=アーカイブをここに展開、サブフォルダを自動検出 +Name[kk]=Мынаған, ішкі қапшығына тарқату +Name[km]=ស្រង់ចេញ​ប័ណ្ណសារ​នៅ​ទីនេះ រកថត​រង​ឃើញ​ដោយ​ស្វ័យ​ប្រវត្តិ +Name[ko]=하위 폴더를 감지해서 여기에 압축 풀기 +Name[lt]=Išpakuoti archyvą čia, automatiškai rasti poaplankius +Name[lv]=Atspiest arhīvu šeit, automātiska apakšmape +Name[mr]=येथे संग्रह पुर्ववत करा, उपसंचयीका स्वंयंशोध +Name[nb]=Pakk ut arkivet her, finn undermapper automatisk +Name[nds]=Archiev hier utpacken, Ünnerorner autom. opdecken +Name[nl]=Archief hier uitpakken, submap autodetecteren +Name[nn]=Pakk ut arkivet her / til undermappe +Name[pa]=ਅਕਾਇਵ ਇੱਥੇ ਖੋਲ੍ਹੋ, ਸਬ-ਫੋਲਡਰ ਆਪੇ ਖੋਜੋ +Name[pl]=Rozpakuj archiwum tutaj, wykryj podkatalogi +Name[pt]=Extrair o Pacote Aqui com Detecção da Sub-Pasta +Name[pt_BR]=Extrair aqui detectando subpasta +Name[ro]=Extrage arhiva aici, autodetectează subdosarul +Name[ru]=Во вложенную папку +Name[sk]=Rozbaliť archív sem, automaticky určiť podpriečinok +Name[sl]=Razširi arhiv sem, samodejno zaznaj podmapo +Name[sq]=Ekstrakto Arkivin Këtu, Vetëdallo Nëndosjen +Name[sr]=Распакуј архиву овде, погоди потфасциклу +Name[sr@ijekavian]=Распакуј архиву овдје, погоди потфасциклу +Name[sr@ijekavianlatin]=Raspakuj arhivu ovdje, pogodi potfasciklu +Name[sr@latin]=Raspakuj arhivu ovde, pogodi potfasciklu +Name[sv]=Packa upp arkiv här, detektera underkatalog automatiskt +Name[th]=คลายแฟ้มจัดเก็บไว้ที่นี่, และตรวจสอบโฟลเดอร์ย่อยอัตโนมัติ +Name[tr]=Arşivi Buraya Çıkart, Alt Dizinleri Otomatik Belirle +Name[uk]=Видобути архів сюди, автоматично визначити підтеку +Name[wa]=Saetchî l' årtchive foû cial, deteccion otomatike do ridant efant +Name[x-test]=xxExtract Archive Here, Autodetect Subfolderxx +Name[zh_CN]=在此解压缩归档,自动探测子文件夹 +Name[zh_TW]=在此解壓縮,自動偵測子資料夾 +Icon=ark +Exec=ark --batch --autodestination --autosubfolder %F diff --git a/ark/app/arkui.rc b/ark/app/arkui.rc new file mode 100644 index 00000000..6775c331 --- /dev/null +++ b/ark/app/arkui.rc @@ -0,0 +1,17 @@ + + + + + + + + + + + + + Main Toolbar + + + + diff --git a/ark/app/batchextract.cpp b/ark/app/batchextract.cpp new file mode 100644 index 00000000..9e466350 --- /dev/null +++ b/ark/app/batchextract.cpp @@ -0,0 +1,292 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2008 Harald Hvaal + * Copyright (C) 2009-2010 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "batchextract.h" + +#include "kerfuffle/archive.h" +#include "kerfuffle/extractiondialog.h" +#include "kerfuffle/jobs.h" +#include "kerfuffle/queries.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +BatchExtract::BatchExtract() + : KCompositeJob(0), + m_autoSubfolder(false), + m_preservePaths(true), + m_openDestinationAfterExtraction(false) +{ + setCapabilities(KJob::Killable); + + connect(this, SIGNAL(result(KJob*)), SLOT(showFailedFiles())); +} + +BatchExtract::~BatchExtract() +{ + if (!m_inputs.isEmpty()) { + KIO::getJobTracker()->unregisterJob(this); + } +} + +void BatchExtract::addExtraction(Kerfuffle::Archive* archive) +{ + QString destination = destinationFolder(); + + if ((autoSubfolder()) && (!archive->isSingleFolderArchive())) { + const QDir d(destination); + QString subfolderName = archive->subfolderName(); + + if (d.exists(subfolderName)) { + subfolderName = KIO::RenameDialog::suggestName(destination, subfolderName); + } + + d.mkdir(subfolderName); + + destination += QLatin1Char( '/' ) + subfolderName; + } + + Kerfuffle::ExtractionOptions options; + options[QLatin1String( "PreservePaths" )] = preservePaths(); + + Kerfuffle::ExtractJob *job = archive->copyFiles(QVariantList(), destination, options); + + kDebug() << QString(QLatin1String( "Registering job from archive %1, to %2, preservePaths %3" )).arg(archive->fileName()).arg(destination).arg(preservePaths()); + + addSubjob(job); + + m_fileNames[job] = qMakePair(archive->fileName(), destination); + + connect(job, SIGNAL(percent(KJob*,ulong)), + this, SLOT(forwardProgress(KJob*,ulong))); + connect(job, SIGNAL(userQuery(Kerfuffle::Query*)), + this, SLOT(slotUserQuery(Kerfuffle::Query*))); +} + +void BatchExtract::slotUserQuery(Kerfuffle::Query *query) +{ + query->execute(); +} + +bool BatchExtract::autoSubfolder() const +{ + return m_autoSubfolder; +} + +void BatchExtract::setAutoSubfolder(bool value) +{ + m_autoSubfolder = value; +} + +void BatchExtract::start() +{ + QTimer::singleShot(0, this, SLOT(slotStartJob())); +} + +void BatchExtract::slotStartJob() +{ + // If none of the archives could be loaded, there is no subjob to run + if (m_inputs.isEmpty()) { + emitResult(); + return; + } + + foreach(Kerfuffle::Archive *archive, m_inputs) { + addExtraction(archive); + } + + KIO::getJobTracker()->registerJob(this); + + emit description(this, + i18n("Extracting file..."), + qMakePair(i18n("Source archive"), m_fileNames.value(subjobs().at(0)).first), + qMakePair(i18n("Destination"), m_fileNames.value(subjobs().at(0)).second) + ); + + m_initialJobCount = subjobs().size(); + + kDebug() << "Starting first job"; + + subjobs().at(0)->start(); +} + +void BatchExtract::showFailedFiles() +{ + if (!m_failedFiles.isEmpty()) { + KMessageBox::informationList(0, i18n("The following files could not be extracted:"), m_failedFiles); + } +} + +void BatchExtract::slotResult(KJob *job) +{ + kDebug(); + + // TODO: The user must be informed about which file caused the error, and that the other files + // in the queue will not be extracted. + if (job->error()) { + kDebug() << "There was en error, " << job->errorText(); + + setErrorText(job->errorText()); + setError(job->error()); + + removeSubjob(job); + + KMessageBox::error(NULL, job->errorText().isEmpty() ? + i18n("There was an error during extraction.") : job->errorText() + ); + + emitResult(); + + return; + } else { + removeSubjob(job); + } + + if (!hasSubjobs()) { + if (openDestinationAfterExtraction()) { + KUrl destination(destinationFolder()); + destination.cleanPath(); + KRun::runUrl(destination, QLatin1String( "inode/directory" ), 0); + } + + kDebug() << "Finished, emitting the result"; + emitResult(); + } else { + kDebug() << "Starting the next job"; + emit description(this, + i18n("Extracting file..."), + qMakePair(i18n("Source archive"), m_fileNames.value(subjobs().at(0)).first), + qMakePair(i18n("Destination"), m_fileNames.value(subjobs().at(0)).second) + ); + subjobs().at(0)->start(); + } +} + +void BatchExtract::forwardProgress(KJob *job, unsigned long percent) +{ + Q_UNUSED(job) + int jobPart = 100 / m_initialJobCount; + setPercent(jobPart *(m_initialJobCount - subjobs().size()) + percent / m_initialJobCount); +} + +bool BatchExtract::addInput(const KUrl& url) +{ + Kerfuffle::Archive *archive = Kerfuffle::Archive::create(url.pathOrUrl(), this); + + if ((archive == NULL) || (!QFileInfo(url.pathOrUrl()).exists())) { + m_failedFiles.append(url.fileName()); + return false; + } + + m_inputs.append(archive); + + return true; +} + +bool BatchExtract::openDestinationAfterExtraction() const +{ + return m_openDestinationAfterExtraction; +} + +bool BatchExtract::preservePaths() const +{ + return m_preservePaths; +} + +QString BatchExtract::destinationFolder() const +{ + if (m_destinationFolder.isEmpty()) { + return QDir::currentPath(); + } else { + return m_destinationFolder; + } +} + +void BatchExtract::setDestinationFolder(const QString& folder) +{ + if (QFileInfo(folder).isDir()) { + m_destinationFolder = folder; + } +} + +void BatchExtract::setOpenDestinationAfterExtraction(bool value) +{ + m_openDestinationAfterExtraction = value; +} + +void BatchExtract::setPreservePaths(bool value) +{ + m_preservePaths = value; +} + +bool BatchExtract::showExtractDialog() +{ + QWeakPointer dialog = + new Kerfuffle::ExtractionDialog; + + if (m_inputs.size() > 1) { + dialog.data()->batchModeOption(); + } + + dialog.data()->setAutoSubfolder(autoSubfolder()); + dialog.data()->setCurrentUrl(destinationFolder()); + dialog.data()->setPreservePaths(preservePaths()); + + if (m_inputs.size() == 1) { + if (m_inputs.at(0)->isSingleFolderArchive()) { + dialog.data()->setSingleFolderArchive(true); + } + dialog.data()->setSubfolder(m_inputs.at(0)->subfolderName()); + } + + if (!dialog.data()->exec()) { + delete dialog.data(); + return false; + } + + setAutoSubfolder(dialog.data()->autoSubfolders()); + setDestinationFolder(dialog.data()->destinationDirectory().pathOrUrl()); + setOpenDestinationAfterExtraction(dialog.data()->openDestinationAfterExtraction()); + setPreservePaths(dialog.data()->preservePaths()); + + delete dialog.data(); + + return true; +} + +#include diff --git a/ark/app/batchextract.h b/ark/app/batchextract.h new file mode 100644 index 00000000..12e35254 --- /dev/null +++ b/ark/app/batchextract.h @@ -0,0 +1,234 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2008 Harald Hvaal + * Copyright (C) 2009-2010 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BATCHEXTRACT_H +#define BATCHEXTRACT_H + +#include +#include + +#include +#include +#include +#include + +namespace Kerfuffle +{ +class Archive; +class Query; +} + +/** + * This class schedules the extraction of all given compressed archives. + * + * Like AddToArchive, this class does not need the GUI to be active, and + * provides the functionality available from the --batch command-line option. + * + * @author Harald Hvaal + */ +class BatchExtract : public KCompositeJob +{ + Q_OBJECT + +public: + /** + * Creates a new BatchExtract object. + */ + BatchExtract(); + + /** + * Destroys a BatchExtract object. + */ + virtual ~BatchExtract(); + + /** + * Creates an ExtractJob for the given @p archive and puts it on the queue. + * + * If necessary, the destination directory for the archive is created. + * + * @param archive The archive that will be extracted. + * + * @see setAutoSubfolder + */ + void addExtraction(Kerfuffle::Archive* archive); + + /** + * A wrapper that calls slotStartJob() when the event loop has started. + */ + void start(); + + /** + * Whether to automatically create a folder inside the destination + * directory if the archive has more than one directory or file + * at top level. + * + * @return @c true Create the subdirectory automatically. + * @return @c false Do not create the subdirectory automatically. + */ + bool autoSubfolder() const; + + /** + * Set whether a folder should be created when necessary so + * the archive is extracted to it. + * + * If set to @c true, when the archive does not consist of a + * single folder with the other files and directories inside, + * a directory will be automatically created inside the destination + * directory and the archive will be extracted there. + * + * @param value Whether to create this directory automatically + * when needed. + */ + void setAutoSubfolder(bool value); + + /** + * Adds a file to the list of files that will be extracted. + * + * @param url The file that will be added to the list. + * + * @return @c true The file exists and a suitable plugin + * could be found for it. + * @return @c false The file does not exist or a suitable + * plugin could not be found. + */ + bool addInput(const KUrl& url); + + /** + * Shows the extract options dialog before extracting the files. + * + * @return @c true The user has set some options and clicked OK. + * @return @c false The user has canceled extraction. + */ + bool showExtractDialog(); + + /** + * Returns the destination directory where the archives + * will be extracted to. + * + * @return The destination directory. If no directory has been manually + * set with setDestinationFolder, QDir::currentPath() will be + * returned. + */ + QString destinationFolder() const; + + /** + * Sets the directory the archives will be extracted to. + * + * If @c setSubfolder has been used, the final destination + * directory will be the concatenation of both. + * + * If @p folder does not exist, the current destination + * folder will not change. + * + * @param folder The directory that will be used. + */ + void setDestinationFolder(const QString& folder); + + /** + * Returns whether the destination folder should + * be open after all archives are extracted. + * + * @return @c true Open the destination folder. + * @return @c false Do not open the destination folder. + */ + bool openDestinationAfterExtraction() const; + + /** + * Whether to open the destination folder after + * all archives are extracted. + * + * @param value Whether to open the destination. + */ + void setOpenDestinationAfterExtraction(bool value); + + /** + * Whether all files should be extracted to the same directory, + * even if they're in different directories in the archive. + * + * This is also known as "flat" extraction. + * + * @return @c true Paths should be preserved. + * @return @c false Paths should be ignored. + */ + bool preservePaths() const; + + /** + * Sets whether paths should be preserved during extraction. + * + * When it is set to false, all files are extracted to a single + * directory, regardless of their hierarchy in the archive. + * + * @param value Whether to preserve paths. + */ + void setPreservePaths(bool value); + +private slots: + /** + * Updates the percentage of the job that has been completed. + */ + void forwardProgress(KJob *job, unsigned long percent); + + /** + * Shows a dialog with a list of all the files that could not + * be successfully extracted. + */ + void showFailedFiles(); + + /** + * Shows an error message if the current job hasn't finished + * successfully, and advances to the next extraction job if + * there are more. + */ + void slotResult(KJob *job); + + /** + * Shows a query dialog, which may happen when a file already exists. + */ + void slotUserQuery(Kerfuffle::Query *query); + + /** + * Does the real work for start() and extracts all scheduled files. + * + * Each extraction job is started after the last one finishes. + * The jobs are executed in the order they were added via addInput(). + */ + void slotStartJob(); + +private: + int m_initialJobCount; + QMap > m_fileNames; + bool m_autoSubfolder; + + QList m_inputs; + QString m_destinationFolder; + QStringList m_failedFiles; + bool m_preservePaths; + bool m_openDestinationAfterExtraction; +}; + +#endif // BATCHEXTRACT_H diff --git a/ark/app/extractHereDndPlugin.cpp b/ark/app/extractHereDndPlugin.cpp new file mode 100644 index 00000000..685a6b7d --- /dev/null +++ b/ark/app/extractHereDndPlugin.cpp @@ -0,0 +1,82 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2009 Harald Hvaal + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "extractHereDndPlugin.h" +#include "batchextract.h" +#include "kerfuffle/archive.h" + +#include +#include +#include +#include +#include +#include + +K_PLUGIN_FACTORY(ExtractHerePluginFactory, + registerPlugin(); + ) +K_EXPORT_PLUGIN(ExtractHerePluginFactory("stupidname", "ark")) + +void ExtractHereDndPlugin::slotTriggered() +{ + kDebug() << "Preparing job"; + BatchExtract *batchJob = new BatchExtract(); + + batchJob->setAutoSubfolder(true); + batchJob->setDestinationFolder(m_dest.pathOrUrl()); + batchJob->setPreservePaths(true); + foreach(const KUrl& url, m_urls) { + batchJob->addInput(url); + } + + batchJob->start(); + kDebug() << "Started job"; + +} + +ExtractHereDndPlugin::ExtractHereDndPlugin(QObject* parent, const QVariantList&) + : KonqDndPopupMenuPlugin(parent) +{ +} + +void ExtractHereDndPlugin::setup(const KFileItemListProperties& popupMenuInfo, + KUrl destination, + QList& userActions) +{ + const QString extractHereMessage = i18nc("@action:inmenu Context menu shown when an archive is being drag'n'dropped", "Extract here"); + + if (!Kerfuffle::supportedMimeTypes().contains(popupMenuInfo.mimeType())) { + kDebug() << popupMenuInfo.mimeType() << "is not a supported mimetype"; + return; + } + + kDebug() << "Plugin executed"; + + KAction *action = new KAction(KIcon(QLatin1String("archive-extract")), + extractHereMessage, NULL); + connect(action, SIGNAL(triggered()), this, SLOT(slotTriggered())); + + userActions.append(action); + m_dest = destination; + m_urls = popupMenuInfo.urlList(); +} + +#include "extractHereDndPlugin.moc" diff --git a/ark/app/extractHereDndPlugin.h b/ark/app/extractHereDndPlugin.h new file mode 100644 index 00000000..0df2bce0 --- /dev/null +++ b/ark/app/extractHereDndPlugin.h @@ -0,0 +1,46 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2009 Harald Hvaal + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef EXTRACTHEREDNDPLUGIN_H +#define EXTRACTHEREDNDPLUGIN_H + +#include +#include + +class ExtractHereDndPlugin : public KonqDndPopupMenuPlugin +{ + Q_OBJECT + +private slots: + void slotTriggered(); + +public: + ExtractHereDndPlugin(QObject* parent, const QVariantList&); + + virtual void setup(const KFileItemListProperties& popupMenuInfo, + KUrl destination, + QList& userActions); +private: + KUrl m_dest; + QList m_urls; +}; + +#endif /* EXTRACTHEREDNDPLUGIN_H */ diff --git a/ark/app/icons/CMakeLists.txt b/ark/app/icons/CMakeLists.txt new file mode 100644 index 00000000..02e3c9ec --- /dev/null +++ b/ark/app/icons/CMakeLists.txt @@ -0,0 +1 @@ +kde4_install_icons(${ICON_INSTALL_DIR}) diff --git a/ark/app/icons/hi128-apps-ark.png b/ark/app/icons/hi128-apps-ark.png new file mode 100644 index 00000000..f74d0cfd Binary files /dev/null and b/ark/app/icons/hi128-apps-ark.png differ diff --git a/ark/app/icons/hi16-apps-ark.png b/ark/app/icons/hi16-apps-ark.png new file mode 100644 index 00000000..7e5d49a6 Binary files /dev/null and b/ark/app/icons/hi16-apps-ark.png differ diff --git a/ark/app/icons/hi22-apps-ark.png b/ark/app/icons/hi22-apps-ark.png new file mode 100644 index 00000000..df43245b Binary files /dev/null and b/ark/app/icons/hi22-apps-ark.png differ diff --git a/ark/app/icons/hi32-apps-ark.png b/ark/app/icons/hi32-apps-ark.png new file mode 100644 index 00000000..81700b8e Binary files /dev/null and b/ark/app/icons/hi32-apps-ark.png differ diff --git a/ark/app/icons/hi48-apps-ark.png b/ark/app/icons/hi48-apps-ark.png new file mode 100644 index 00000000..373f2c38 Binary files /dev/null and b/ark/app/icons/hi48-apps-ark.png differ diff --git a/ark/app/icons/hi64-apps-ark.png b/ark/app/icons/hi64-apps-ark.png new file mode 100644 index 00000000..1f7b5378 Binary files /dev/null and b/ark/app/icons/hi64-apps-ark.png differ diff --git a/ark/app/icons/hisc-apps-ark.svgz b/ark/app/icons/hisc-apps-ark.svgz new file mode 100644 index 00000000..e54bb92c Binary files /dev/null and b/ark/app/icons/hisc-apps-ark.svgz differ diff --git a/ark/app/main.cpp b/ark/app/main.cpp new file mode 100644 index 00000000..bcd187d2 --- /dev/null +++ b/ark/app/main.cpp @@ -0,0 +1,210 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2007 Henrique Pinto + * Copyright (C) 2008-2009 Harald Hvaal + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "mainwindow.h" +#include "batchextract.h" +#include "kerfuffle/addtoarchive.h" + +#include +#include +#include +#include +#include + +#include +#include + +using Kerfuffle::AddToArchive; + +int main(int argc, char **argv) +{ + KAboutData aboutData("ark", 0, ki18n("Ark"), + "2.19", ki18n("KDE Archiving tool"), + KAboutData::License_GPL, + ki18n("(c) 1997-2011, The Various Ark Developers"), + KLocalizedString(), + "http://utils.kde.org/projects/ark" + ); + + aboutData.addAuthor(ki18n("Raphael Kubo da Costa"), + ki18n("Maintainer"), + "rakuco@FreeBSD.org"); + aboutData.addAuthor(ki18n("Harald Hvaal"), + ki18n("Former Maintainer"), + "haraldhv@stud.ntnu.no"); + aboutData.addAuthor(ki18n("Henrique Pinto"), + ki18n("Former Maintainer"), + "henrique.pinto@kdemail.net"); + aboutData.addAuthor(ki18n("Helio Chissini de Castro"), + ki18n("Former maintainer"), + "helio@kde.org"); + aboutData.addAuthor(ki18n("Georg Robbers"), + KLocalizedString(), + "Georg.Robbers@urz.uni-hd.de"); + aboutData.addAuthor(ki18n("Roberto Selbach Teixeira"), + KLocalizedString(), + "maragato@kde.org"); + aboutData.addAuthor(ki18n("Francois-Xavier Duranceau"), + KLocalizedString(), + "duranceau@kde.org"); + aboutData.addAuthor(ki18n("Emily Ezust (Corel Corporation)"), + KLocalizedString(), + "emilye@corel.com"); + aboutData.addAuthor(ki18n("Michael Jarrett (Corel Corporation)"), + KLocalizedString(), + "michaelj@corel.com"); + aboutData.addAuthor(ki18n("Robert Palmbos"), + KLocalizedString(), + "palm9744@kettering.edu"); + + aboutData.addCredit(ki18n("Bryce Corkins"), + ki18n("Icons"), + "dbryce@attglobal.net"); + aboutData.addCredit(ki18n("Liam Smit"), + ki18n("Ideas, help with the icons"), + "smitty@absamail.co.za"); + aboutData.addCredit(ki18n("Andrew Smith"), + ki18n("bkisofs code"), + QByteArray(), + "http://littlesvr.ca/misc/contactandrew.php"); + aboutData.setProgramIconName(QLatin1String("ark")); + + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineOptions option; + option.add("+[url]", ki18n("URL of an archive to be opened")); + option.add("d").add("dialog", ki18n("Show a dialog for specifying the options for the operation (extract/add)")); + option.add("o").add("destination ", ki18n("Destination folder to extract to. Defaults to current path if not specified.")); + option.add(":", ki18n("Options for adding files")); + option.add("c").add("add", ki18n("Query the user for an archive filename and add specified files to it. Quit when finished.")); + option.add("t").add("add-to ", ki18n("Add the specified files to 'filename'. Create archive if it does not exist. Quit when finished.")); + option.add("p").add("changetofirstpath", ki18n("Change the current dir to the first entry and add all other entries relative to this one.")); + option.add("f").add("autofilename ", ki18n("Automatically choose a filename, with the selected suffix (for example rar, tar.gz, zip or any other supported types)")); + option.add(":", ki18n("Options for batch extraction:")); + option.add("b").add("batch", ki18n("Use the batch interface instead of the usual dialog. This option is implied if more than one url is specified.")); + option.add("e").add("autodestination", ki18n("The destination argument will be set to the path of the first file supplied.")); + option.add("a").add("autosubfolder", ki18n("Archive contents will be read, and if detected to not be a single folder archive, a subfolder with the name of the archive will be created.")); + KCmdLineArgs::addCmdLineOptions(option); + KCmdLineArgs::addTempFileOption(); + + KApplication application; + application.setQuitOnLastWindowClosed(false); + + //session restoring + if (application.isSessionRestored()) { + MainWindow* window = NULL; + + if (KMainWindow::canBeRestored(1)) { + window = new MainWindow; + window->restore(1); + if (!window->loadPart()) { + delete window; + window = NULL; + } + } + + if (window == NULL) { + return -1; + } + } else { //new ark window (no restored session) + // open any given URLs + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + if (args->isSet("add") || args->isSet("add-to")) { + AddToArchive *addToArchiveJob = new AddToArchive; + application.connect(addToArchiveJob, SIGNAL(result(KJob*)), SLOT(quit()), Qt::QueuedConnection); + + if (args->isSet("changetofirstpath")) { + addToArchiveJob->setChangeToFirstPath(true); + } + + if (args->isSet("add-to")) { + addToArchiveJob->setFilename(args->getOption("add-to")); + } + + if (args->isSet("autofilename")) { + addToArchiveJob->setAutoFilenameSuffix(args->getOption("autofilename")); + } + + for (int i = 0; i < args->count(); ++i) { + //TODO: use the returned value here? + addToArchiveJob->addInput(args->url(i)); + } + + if (args->isSet("dialog")) { + if (!addToArchiveJob->showAddDialog()) { + return 0; + } + } + + addToArchiveJob->start(); + } else if (args->isSet("batch")) { + BatchExtract *batchJob = new BatchExtract; + application.connect(batchJob, SIGNAL(result(KJob*)), SLOT(quit()), Qt::QueuedConnection); + + for (int i = 0; i < args->count(); ++i) { + batchJob->addInput(args->url(i)); + } + + if (args->isSet("autosubfolder")) { + kDebug() << "Setting autosubfolder"; + batchJob->setAutoSubfolder(true); + } + + if (args->isSet("autodestination")) { + QString autopath = QFileInfo(args->url(0).path()).path(); + kDebug() << "By autodestination, setting path to " << autopath; + batchJob->setDestinationFolder(autopath); + } + + if (args->isSet("destination")) { + kDebug() << "Setting destination to " << args->getOption("destination"); + batchJob->setDestinationFolder(args->getOption("destination")); + } + + if (args->isSet("dialog")) { + if (!batchJob->showExtractDialog()) { + return 0; + } + } + + batchJob->start(); + } else { + MainWindow *window = new MainWindow; + if (!window->loadPart()) { // if loading the part fails + return -1; + } + + if (args->count()) { + kDebug() << "trying to open" << args->url(0); + + if (args->isSet("dialog")) { + window->setShowExtractDialog(true); + } + window->openUrl(args->url(0)); + } + window->show(); + } + } + + kDebug() << "Entering application loop"; + return application.exec(); +} diff --git a/ark/app/mainwindow.cpp b/ark/app/mainwindow.cpp new file mode 100644 index 00000000..aee82317 --- /dev/null +++ b/ark/app/mainwindow.cpp @@ -0,0 +1,259 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2002-2003: Georg Robbers + * Copyright (C) 2003: Helio Chissini de Castro + * Copyright (C) 2007 Henrique Pinto + * Copyright (C) 2008 Harald Hvaal + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "mainwindow.h" +#include "kerfuffle/archive.h" +#include "part/interface.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static bool isValidArchiveDrag(const QMimeData *data) +{ + return ((data->hasUrls()) && (data->urls().count() == 1)); +} + +MainWindow::MainWindow(QWidget *) + : KParts::MainWindow() +{ + setXMLFile(QLatin1String( "arkui.rc" )); + + setupActions(); + statusBar(); + + if (!initialGeometrySet()) { + resize(640, 480); + } + setAutoSaveSettings(QLatin1String( "MainWindow" )); + + setAcceptDrops(true); +} + +MainWindow::~MainWindow() +{ + if (m_recentFilesAction) { + m_recentFilesAction->saveEntries(KGlobal::config()->group("Recent Files")); + } + delete m_part; + m_part = 0; +} + +void MainWindow::dragEnterEvent(QDragEnterEvent * event) +{ + kDebug() << event; + + Interface *iface = qobject_cast(m_part); + if (iface->isBusy()) { + return; + } + + if ((event->source() == NULL) && + (isValidArchiveDrag(event->mimeData()))) { + event->acceptProposedAction(); + } + return; +} + +void MainWindow::dropEvent(QDropEvent * event) +{ + kDebug() << event; + + Interface *iface = qobject_cast(m_part); + if (iface->isBusy()) { + return; + } + + if ((event->source() == NULL) && + (isValidArchiveDrag(event->mimeData()))) { + event->acceptProposedAction(); + } + + //TODO: if this call provokes a message box the drag will still be going + //while the box is onscreen. looks buggy, do something about it + openUrl(event->mimeData()->urls().at(0)); +} + +void MainWindow::dragMoveEvent(QDragMoveEvent * event) +{ + kDebug() << event; + + Interface *iface = qobject_cast(m_part); + if (iface->isBusy()) { + return; + } + + if ((event->source() == NULL) && + (isValidArchiveDrag(event->mimeData()))) { + event->acceptProposedAction(); + } +} + +bool MainWindow::loadPart() +{ + KPluginLoader loader(QLatin1String( "arkpart" )); + KPluginFactory *factory = loader.factory(); + if (factory) { + m_part = static_cast(factory->create(this)); + } + if (!factory || !m_part) { + KMessageBox::error(this, i18n("Unable to find Ark's KPart component, please check your installation.")); + kWarning() << "Error loading Ark KPart: " << loader.errorString(); + return false; + } + + m_part->setObjectName( QLatin1String("ArkPart" )); + setCentralWidget(m_part->widget()); + createGUI(m_part); + + connect(m_part, SIGNAL(busy()), this, SLOT(updateActions())); + connect(m_part, SIGNAL(ready()), this, SLOT(updateActions())); + connect(m_part, SIGNAL(quit()), this, SLOT(quit())); + + return true; +} + +void MainWindow::setupActions() +{ + m_newAction = KStandardAction::openNew(this, SLOT(newArchive()), actionCollection()); + m_openAction = KStandardAction::open(this, SLOT(openArchive()), actionCollection()); + KStandardAction::quit(this, SLOT(quit()), actionCollection()); + + m_recentFilesAction = KStandardAction::openRecent(this, SLOT(openUrl(KUrl)), actionCollection()); + m_recentFilesAction->setToolBarMode(KRecentFilesAction::MenuMode); + m_recentFilesAction->setToolButtonPopupMode(QToolButton::DelayedPopup); + m_recentFilesAction->setIconText(i18nc("action, to open an archive", "Open")); + m_recentFilesAction->setStatusTip(i18n("Click to open an archive, click and hold to open a recently-opened archive")); + m_recentFilesAction->setToolTip(i18n("Open an archive")); + m_recentFilesAction->loadEntries(KGlobal::config()->group("Recent Files")); + connect(m_recentFilesAction, SIGNAL(triggered()), + this, SLOT(openArchive())); + + createStandardStatusBarAction(); + + KStandardAction::configureToolbars(this, SLOT(editToolbars()), actionCollection()); + KStandardAction::keyBindings(this, SLOT(editKeyBindings()), actionCollection()); +} + +void MainWindow::updateActions() +{ + Interface *iface = qobject_cast(m_part); + m_newAction->setEnabled(!iface->isBusy()); + m_openAction->setEnabled(!iface->isBusy()); + m_recentFilesAction->setEnabled(!iface->isBusy()); +} + +void MainWindow::editKeyBindings() +{ + KShortcutsDialog dlg(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this); + dlg.addCollection(actionCollection()); + dlg.addCollection(m_part->actionCollection()); + + dlg.configure(); +} + +void MainWindow::editToolbars() +{ + saveMainWindowSettings(KGlobal::config()->group(QLatin1String("MainWindow"))); + + QWeakPointer dlg = new KEditToolBar(factory(), this); + dlg.data()->exec(); + + createGUI(m_part); + + applyMainWindowSettings(KGlobal::config()->group(QLatin1String("MainWindow"))); + + delete dlg.data(); +} + +void MainWindow::openArchive() +{ + Interface *iface = qobject_cast(m_part); + Q_ASSERT(iface); + const KUrl url = KFileDialog::getOpenUrl(KUrl("kfiledialog:///ArkOpenDir"), + Kerfuffle::supportedMimeTypes().join( QLatin1String( " " )), + this); + openUrl(url); +} + +void MainWindow::openUrl(const KUrl& url) +{ + if (!url.isEmpty()) { + m_part->setArguments(m_openArgs); + + if (m_part->openUrl(url)) { + m_recentFilesAction->addUrl(url); + } else { + m_recentFilesAction->removeUrl(url); + } + } +} + +void MainWindow::setShowExtractDialog(bool option) +{ + if (option) { + m_openArgs.metaData()[QLatin1String( "showExtractDialog" )] = QLatin1String( "true" ); + } else { + m_openArgs.metaData().remove(QLatin1String( "showExtractDialog" )); + } +} + +void MainWindow::quit() +{ + close(); +} + +void MainWindow::newArchive() +{ + Interface *iface = qobject_cast(m_part); + Q_ASSERT(iface); + + const QStringList mimeTypes = Kerfuffle::supportedWriteMimeTypes(); + + kDebug() << "Supported mimetypes are" << mimeTypes.join( QLatin1String( " " )); + + const KUrl saveFileUrl = + KFileDialog::getSaveUrl(KUrl("kfiledialog:///ArkNewDir"), + mimeTypes.join(QLatin1String(" "))); + + m_openArgs.metaData()[QLatin1String( "createNewArchive" )] = QLatin1String( "true" ); + + openUrl(saveFileUrl); + + m_openArgs.metaData().remove(QLatin1String( "showExtractDialog" )); + m_openArgs.metaData().remove(QLatin1String( "createNewArchive" )); +} diff --git a/ark/app/mainwindow.h b/ark/app/mainwindow.h new file mode 100644 index 00000000..5dd7fb82 --- /dev/null +++ b/ark/app/mainwindow.h @@ -0,0 +1,66 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2007 Henrique Pinto + * Copyright (C) 2008 Harald Hvaal + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include + +class KRecentFilesAction; + +class MainWindow: public KParts::MainWindow +{ + Q_OBJECT +public: + MainWindow(QWidget *parent = 0); + ~MainWindow(); + bool loadPart(); + + void dragEnterEvent(class QDragEnterEvent * event); + void dropEvent(class QDropEvent * event); + void dragMoveEvent(class QDragMoveEvent * event); + +public slots: + void openUrl(const KUrl& url); + void setShowExtractDialog(bool); + +private slots: + void updateActions(); + void newArchive(); + void openArchive(); + void quit(); + + void editKeyBindings(); + void editToolbars(); + +private: + void setupActions(); + + KParts::ReadWritePart *m_part; + KRecentFilesAction *m_recentFilesAction; + QAction *m_openAction; + QAction *m_newAction; + KParts::OpenUrlArguments m_openArgs; +}; + +#endif // MAINWINDOW_H diff --git a/ark/cmake/modules/COPYING-CMAKE-SCRIPTS b/ark/cmake/modules/COPYING-CMAKE-SCRIPTS new file mode 100644 index 00000000..4b417765 --- /dev/null +++ b/ark/cmake/modules/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,22 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ark/cmake/modules/FindLibArchive.cmake b/ark/cmake/modules/FindLibArchive.cmake new file mode 100644 index 00000000..db14d7b2 --- /dev/null +++ b/ark/cmake/modules/FindLibArchive.cmake @@ -0,0 +1,46 @@ +# - Try to find libarchive +# Once done this will define +# +# LIBARCHIVE_FOUND - system has libarchive +# LIBARCHIVE_INCLUDE_DIR - the libarchive include directory +# LIBARCHIVE_LIBRARY - Link this to use libarchive +# HAVE_LIBARCHIVE_GZIP_SUPPORT - whether libarchive has been compiled with gzip support +# HAVE_LIBARCHIVE_LZMA_SUPPORT - whether libarchive has been compiled with lzma support +# HAVE_LIBARCHIVE_XZ_SUPPORT - whether libarchive has been compiled with xz support +# HAVE_LIBARCHIVE_RPM_SUPPORT - whether libarchive has been compiled with rpm support +# HAVE_LIBARCHIVE_CAB_SUPPORT - whether libarchive has been compiled with cab support +# +# Copyright (c) 2006, Pino Toscano, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(CheckLibraryExists) + +if (LIBARCHIVE_LIBRARY AND LIBARCHIVE_INCLUDE_DIR) + # in cache already + set(LIBARCHIVE_FOUND TRUE) +else (LIBARCHIVE_LIBRARY AND LIBARCHIVE_INCLUDE_DIR) + find_path(LIBARCHIVE_INCLUDE_DIR archive.h + ${GNUWIN32_DIR}/include + ) + + find_library(LIBARCHIVE_LIBRARY NAMES archive libarchive archive2 libarchive2 + PATHS + ${GNUWIN32_DIR}/lib + ) + + if (LIBARCHIVE_LIBRARY) + check_library_exists(${LIBARCHIVE_LIBRARY} archive_write_set_compression_gzip "" HAVE_LIBARCHIVE_GZIP_SUPPORT) + check_library_exists(${LIBARCHIVE_LIBRARY} archive_write_set_compression_lzma "" HAVE_LIBARCHIVE_LZMA_SUPPORT) + check_library_exists(${LIBARCHIVE_LIBRARY} archive_write_set_compression_xz "" HAVE_LIBARCHIVE_XZ_SUPPORT) + check_library_exists(${LIBARCHIVE_LIBRARY} archive_read_support_compression_rpm "" HAVE_LIBARCHIVE_RPM_SUPPORT) + check_library_exists(${LIBARCHIVE_LIBRARY} archive_read_disk_entry_from_file "" HAVE_LIBARCHIVE_READ_DISK_API) + check_library_exists(${LIBARCHIVE_LIBRARY} archive_read_support_format_cab "" HAVE_LIBARCHIVE_CAB_SUPPORT) + endif (LIBARCHIVE_LIBRARY) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(LibArchive DEFAULT_MSG LIBARCHIVE_INCLUDE_DIR LIBARCHIVE_LIBRARY HAVE_LIBARCHIVE_GZIP_SUPPORT) + + mark_as_advanced(LIBARCHIVE_INCLUDE_DIR LIBARCHIVE_LIBRARY HAVE_LIBARCHIVE_GZIP_SUPPORT HAVE_LIBARCHIVE_LZMA_SUPPORT HAVE_LIBARCHIVE_RPM_SUPPORT HAVE_LIBARCHIVE_CAB_SUPPORT) +endif (LIBARCHIVE_LIBRARY AND LIBARCHIVE_INCLUDE_DIR) diff --git a/ark/config.h.cmake b/ark/config.h.cmake new file mode 100644 index 00000000..1d0b1cb4 --- /dev/null +++ b/ark/config.h.cmake @@ -0,0 +1,2 @@ +#cmakedefine HAVE_LIBARCHIVE_LZMA_SUPPORT ${HAVE_LIBARCHIVE_LZMA_SUPPORT} +#cmakedefine HAVE_LIBARCHIVE_XZ_SUPPORT ${HAVE_LIBARCHIVE_XZ_SUPPORT} diff --git a/ark/doc/CMakeLists.txt b/ark/doc/CMakeLists.txt new file mode 100644 index 00000000..7bcd32ef --- /dev/null +++ b/ark/doc/CMakeLists.txt @@ -0,0 +1,5 @@ +########### install files ############### +# +kde4_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR ark) +kde4_create_manpage(man-ark.1.docbook 1 INSTALL_DESTINATION ${MAN_INSTALL_DIR}) + diff --git a/ark/doc/ark-mainwindow.png b/ark/doc/ark-mainwindow.png new file mode 100644 index 00000000..0fe438f1 Binary files /dev/null and b/ark/doc/ark-mainwindow.png differ diff --git a/ark/doc/index.docbook b/ark/doc/index.docbook new file mode 100644 index 00000000..0e52c953 --- /dev/null +++ b/ark/doc/index.docbook @@ -0,0 +1,336 @@ + + + + + +]> + + + + +The &ark; Handbook + + + +&Matt.Johnston; &Matt.Johnston.mail; + + + + + + +2000 +&Matt.Johnston; + + + +2004 +Henrique Pinto + + + +&FDLNotice; + +2013-06-21 +2.19 &kde; (4.11) + + +&ark; is an archive manager for &kde;. + + +KDE +gzip +gunzip +tar +archive +zip +compression +7z +kdeutils +ark + + + + +Introduction + +&ark; is a program for viewing, extracting, creating and modifying +archives. +&ark; can handle various archive formats such as +tar, gzip, +bzip2, zip, rar, +7zip, xz, rpm, +cab and deb (support for certain archive formats depend on +the appropriate command-line programs being installed). + + +&ark;'s main window + + + + + +&ark;'s main window + + + + + + + +Using &ark; + + +Opening Archives + +To open an archive in &ark;, choose +Open... (&Ctrl;O) from the File +menu. You can also open archive files by dragging and dropping from +&konqueror; or &dolphin;. Archive files should be associated with &ark;, so you can +also right click a file in &konqueror; or &dolphin; and +select Open with &ark; to open it or select an extract action for this file. + +If you have enabled the information panel in the Settings menu +additional information about the selected folders or files in the archive is displayed. + + + +Working with Files + +Once an archive has been opened, you can perform various +operations on the files inside the archive. By +selecting a file and using the Action +menu, you can choose what you want to do: + + + +Add File... will add files from your disk to the archive. + + +Add Folder... will add folders from your disk to the archive. + + +Delete (Del) will remove the currently +selected file(s) from the archive. + + +Extract... (&Ctrl;E) opens a submenu with previously accessed folders, +and you can select to quick extract into any of them. + + +Preview will open the file in the associated viewer for that file type. + + + + + + +Extracting Archives and Removing Files + +Once an archive has been opened in &ark;, it can be extracted. To +quick extract files from an archive, you can select +Extract from the +Action menu. This opens a submenu with previously accessed folders, +and you can select to quick extract into any of them. Clicking on the check mark at +the right side of the Extract button in the toolbar performes the same action. + + +To open the Extract dialog click the Extract +button in the toolbar or use the shortcut &Ctrl; +E. + +This dialog allows you to select where you will extract files to. The default +location is the folder the archive is in. +You can specify to extract the files into a subfolder. The default name of this +subfolder is the first part of the archive name, but you can edit it to your needs. +If you want to preserve paths when extracting, select this option. +You may also choose to open the destination folder +in &konqueror; or &dolphin; or close &ark; once the extraction is complete. + +If a file in the archive list is highlighted, you can +also select which files to extract: + + + +Selected files only extracts only the files +which have been selected. + + +All files extracts the entire contents of the +archive. + + +To extract a single file from an archive, click on the filename and drag it to the +target folder + + +Extracting files from an archive does not change the archive and its contents. +Use ActionDelete +(Del) for this task. + + + + + +Creating Archives and Adding Files + +To create a new archive in &ark;, choose +New from the File +menu. + +You can then type the name of the archive, with the appropriate +extension (tar.gz, zip, bz2 +&etc;) or select a supported format in the Filter combo box +and check the Automatically select filename extension option. +To add files to the archive, choose Add +File... from the Action menu. If you +want to add an entire folder to an archive, choose Add +Folder... from the Action menu. + +An alternative way to add files to the archive is to drag one or more files +from &konqueror;, &dolphin; or the desktop into the main &ark; window, and it will +be added to the current archive. + + + + + +Using &ark; in the Filemanager + +Clicking with the &RMB; on an archive in a filemanager like &dolphin; or &konqueror; +displays a context menu with an item Open with Ark. +The menu has these additional items to extract an archive using &ark;: + + + + +Extract Archive Here, Autodetect Subfolder creates a +subfolder in the folder with the archive and extracts the folders and files into it. + + +Extract Archive To... works like in &ark;. + + +Extract Archive Here extracts the content of the archive into same folder. + + + + +&dolphin;'s or &konqueror;'s context menu for a selection displays these actions in the +Compress submenu: + + + + +Here creates a Tar archive (gzip-compressed) in the current folder +with the extension .tar.gz. + + +As ZIP Archive, As RAR Archive +or As ZIP/TAR Archive creates these archive types in the current folder. + + +Compress To... opens a dialog where you can select folder, name and archive type. + + + + + + +Advanced Batch Mode +&ark; has an advanced batch mode to manage archives without launching a &GUI; interface. +This mode allows you to extract or create archives and add files to them. + +The batch mode is documented in &ark;'s man page. + + + + + +Credits and License + +&ark; is Copyright © 1997-2008, The Various &ark; Developers + + +Authors: +Raphael Kubo da Costa +rakuco@FreeBSD.org +Harald Hvaal +haraldhv@stud.ntnu.no +Helio Chissini de Castro +helio@conectiva.com.br +Georg Robbers +Georg.Robbers@urz.uni-hd.de +Henrique Pinto +henrique.pinto@kdemail.net +Roberto Selbach Teixeira +maragato@kde.org +Robert Palmbos +palm9744@kettering.edu +Francois-Xavier Duranceau +duranceau@kde.org +Corel Corporation (author: Emily Ezust) +emilye@corel.com +Corel Corporation (author: Michael Jarrett) +michaelj@corel.com + + +Documentation Copyright © 2000 &Matt.Johnston; +&Matt.Johnston.mail; + +Documentation updated for &kde; 3.3 by Henrique Pinto +henrique.pinto@kdemail.net. + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &ark; + +&install.intro.documentation; + + + + +Requirements + +In order to successfully use &ark;, you need &kde; +4. &GNU; tar and a recent +gzip are also needed if you want &ark; to handle tar archives. To handle other +file formats, you need the appropriate command line programs, such as bzip2, +zip, unzip, ar, rar, +7zip, xz, rpm, +cab and deb. + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + +&documentation.index; + + + + diff --git a/ark/doc/man-ark.1.docbook b/ark/doc/man-ark.1.docbook new file mode 100644 index 00000000..22f8519b --- /dev/null +++ b/ark/doc/man-ark.1.docbook @@ -0,0 +1,208 @@ + + + +]> + + + +&kde; User's Manual +LauriWatts Initial version of &ark; man page. + +RaphaelKubo da Costa Update &ark; man page. + +2013-08-23 +2.19 (&kde; 4.11) +K Desktop Environment + + + +ark +1 + + + +ark +&kde; archiving tool + + + + +ark + + + + + +suffix + + +file + + +directory +&kde; Generic Options +&Qt; Generic Options + + + + +Description +&ark; is a program for managing various compressed file formats +within &kde;. Archives can be viewed, extracted, created +and modified with &ark;. The program can handle various +formats such as tar, +gzip, bzip2, +zip, rar +(when the appropriate libraries or command-line programs are +installed). + + + +Operation modes +&ark; can be used either as a stand-alone &GUI; program as well as a +command-line program in order to perform some specific tasks. +If invoked without the -b (--batch) or -c (--add) options, &ark; is started +as a normal &GUI; program. +When the -b (--batch) option is used, &ark; can be used to extract the +contents of one or more files directly from the command-line, without +launching its &GUI;. +When the -c (--add) option is used, &ark; prompts for files that should +be added to a new archive or to an existing archive. + + + + + +Options + + + + + +Show a dialog for specifying the options for a batch or add operation. + + + + +Default the extraction directory to directory. +If not passed, the current path is used. + + + + + +Options for adding files + + + + +Query the user for an archive filename and add specified files to it. +Quit when finished. + + + + + +Add the specified files to filename. Create archive +if it does not exist. Quit when finished. + + + + + +Change the current directory to the first entry and add all other entries relative +to this one. + + + + + + +Automatically choose a filename, with the selected suffix +(for example rar, tar.gz, zip or any other supported types). + + + + + + +Options for batch extraction + + + + +Use the batch interface instead of the usual dialog. This option is implied +if more than one url is specified. + + + + + + +The destination argument will be set to the path of the first file +supplied. + + + + + + +Archive contents will be read, and if detected to not be a single folder archive, +a subfolder by the name of the archive will be created. + + + + + + + + +Examples + + + +ark +archive.tar.bz2 + +Will extract archive.tar.bz2 into the current directory +without showing any &GUI;. + + + + + +ark +archive.tar.bz2 archive2.zip + +Will first show an extraction options dialog and then extract both +archive.tar.bz2 and archive2.zip +into the directory chosen in the dialog. + + + + +ark +my-archive.zip photo1.jpg +text.txt + +Will create my-archive.zip if does not exist and +then add photo1.jpg and text.txt to it. + + + + + + + + +Authors +&ark; is currently maintained by &Harald.Hvaal; &Harald.Hvaal.mail; +and &Raphael.Kubo.da.Costa; &Raphael.Kubo.da.Costa.mail;. +This man page was first written by &Lauri.Watts; +&Lauri.Watts.mail; in 2005 for &kde; 3.4, and was later updated in 2009 by +&Raphael.Kubo.da.Costa; &Raphael.Kubo.da.Costa.mail;. + + + diff --git a/ark/kerfuffle/CMakeLists.txt b/ark/kerfuffle/CMakeLists.txt new file mode 100644 index 00000000..8c56b4f4 --- /dev/null +++ b/ark/kerfuffle/CMakeLists.txt @@ -0,0 +1,50 @@ +macro_optional_find_package(QJSON) +macro_log_feature(QJSON_FOUND "qjson" "A library for processing and serializing JSON files" "http://qjson.sourceforge.net" FALSE "" "Required for compiling Ark's unit tests") + +########### next target ############### + +set(kerfuffle_SRCS + archive.cpp + archiveinterface.cpp + jobs.cpp + extractiondialog.cpp + adddialog.cpp + queries.cpp + addtoarchive.cpp + cliinterface.cpp + ) + +kde4_add_kcfg_files(kerfuffle_SRCS settings.kcfgc) + +kde4_add_ui_files(kerfuffle_SRCS extractiondialog.ui adddialog.ui ) + +kde4_add_library(kerfuffle SHARED ${kerfuffle_SRCS}) + +target_link_libraries(kerfuffle ${KDE4_KFILE_LIBS} ${KDE4_KPARTS_LIBS}) +if (NOT WIN32) + target_link_libraries(kerfuffle ${KDE4_KPTY_LIBS}) +endif (NOT WIN32) + +set_target_properties(kerfuffle PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION}) + +install(TARGETS kerfuffle ${INSTALL_TARGETS_DEFAULT_ARGS}) + +install(FILES kerfufflePlugin.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR}) +install(FILES ark.kcfg DESTINATION ${KCFG_INSTALL_DIR}) + +if (QJSON_FOUND) + # This is a hack to make QJSON work with both 0.7.1 (the latest stable) + # and the current master (b440550), which uses a different casing for + # the CMake variables. + # It should be removed when QJSON master becomes sane and reverts the + # casing again. + if (QJSON_LIBRARIES AND QJSON_INCLUDE_DIR) + set(KERFUFFLE_QJSON_LIBRARIES "${QJSON_LIBRARIES}") + set(KERFUFFLE_QJSON_INCLUDE_DIR "${QJSON_INCLUDE_DIR}") + else (QJSON_LIBRARIES AND QJSON_INCLUDE_DIR) + set(KERFUFFLE_QJSON_LIBRARIES "${qjson_LIBRARIES}") + set(KERFUFFLE_QJSON_INCLUDE_DIR "${qjson_INCLUDE_DIR}") + endif (QJSON_LIBRARIES AND QJSON_INCLUDE_DIR) + + add_subdirectory(tests) +endif (QJSON_FOUND) diff --git a/ark/kerfuffle/adddialog.cpp b/ark/kerfuffle/adddialog.cpp new file mode 100644 index 00000000..2950ee6e --- /dev/null +++ b/ark/kerfuffle/adddialog.cpp @@ -0,0 +1,135 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2008 Harald Hvaal + * Copyright (C) 2009,2011 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "adddialog.h" +#include "ui_adddialog.h" +#include "kerfuffle/archive.h" + +#include +#include +#include + +#include +#include + +namespace Kerfuffle +{ +class AddDialogUI: public QWidget, public Ui::AddDialog +{ +public: + AddDialogUI(QWidget *parent = 0) + : QWidget(parent) { + setupUi(this); + } +}; + +AddDialog::AddDialog(const QStringList& itemsToAdd, + const KUrl & startDir, + const QString & filter, + QWidget * parent, + QWidget * widget + ) + : KFileDialog(startDir, filter, parent, widget) +{ + setOperationMode(KFileDialog::Saving); + setMode(KFile::File | KFile::LocalOnly); + setConfirmOverwrite(true); + setCaption(i18n("Compress to Archive")); + + loadConfiguration(); + + connect(this, SIGNAL(okClicked()), SLOT(updateDefaultMimeType())); + + m_ui = new AddDialogUI(this); + mainWidget()->layout()->addWidget(m_ui); + + setupIconList(itemsToAdd); + + // Set up a default name if there's only one file to compress + if (itemsToAdd.size() == 1) { + const QFileInfo fileInfo(itemsToAdd.first()); + const QString fileName = + fileInfo.isDir() ? fileInfo.dir().dirName() : fileInfo.baseName(); + + // #272914: Add an extension when it is present, otherwise KFileDialog + // will not automatically add it as baseFileName is a file which + // already exists. + setSelection(fileName + currentFilterMimeType()->mainExtension()); + } + + //These extra options will be implemented in a 4.2+ version of + //ark + m_ui->groupExtraOptions->hide(); +} + +void AddDialog::loadConfiguration() +{ + m_config = KConfigGroup(KGlobal::config()->group("AddDialog")); + + const QString defaultMimeType = QLatin1String( "application/x-compressed-tar" ); + const QStringList writeMimeTypes = Kerfuffle::supportedWriteMimeTypes(); + const QString lastMimeType = m_config.readEntry("LastMimeType", defaultMimeType); + + if (writeMimeTypes.contains(lastMimeType)) { + setMimeFilter(writeMimeTypes, lastMimeType); + } else { + setMimeFilter(writeMimeTypes, defaultMimeType); + } +} + +void AddDialog::setupIconList(const QStringList& itemsToAdd) +{ + QStandardItemModel* listModel = new QStandardItemModel(this); + QStringList sortedList(itemsToAdd); + + sortedList.sort(); + + Q_FOREACH(const QString& urlString, sortedList) { + KUrl url(urlString); + + QStandardItem* item = new QStandardItem; + item->setText(url.fileName()); + + QString iconName = KMimeType::iconNameForUrl(url); + item->setIcon(KIcon(iconName)); + + item->setData(QVariant(url), KFilePlacesModel::UrlRole); + + listModel->appendRow(item); + } + + m_ui->compressList->setModel(listModel); +} + +void AddDialog::updateDefaultMimeType() +{ + m_config.writeEntry("LastMimeType", currentMimeFilter()); +} +} + +#include "adddialog.moc" diff --git a/ark/kerfuffle/adddialog.h b/ark/kerfuffle/adddialog.h new file mode 100644 index 00000000..7c9f03e9 --- /dev/null +++ b/ark/kerfuffle/adddialog.h @@ -0,0 +1,63 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2008 Harald Hvaal + * Copyright (C) 2009 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ADDDIALOG_H +#define ADDDIALOG_H + +#include "kerfuffle_export.h" + +#include +#include + +namespace Kerfuffle +{ +class KERFUFFLE_EXPORT AddDialog : public KFileDialog +{ + Q_OBJECT + +public: + AddDialog(const QStringList & itemsToAdd, + const KUrl & startDir, + const QString & filter, + QWidget * parent, + QWidget * widget = 0 + ); + +private: + class AddDialogUI *m_ui; + KConfigGroup m_config; + + void loadConfiguration(); + void setupIconList(const QStringList& itemsToAdd); + +private slots: + void updateDefaultMimeType(); +}; +} + +#endif diff --git a/ark/kerfuffle/adddialog.ui b/ark/kerfuffle/adddialog.ui new file mode 100644 index 00000000..a1c595b8 --- /dev/null +++ b/ark/kerfuffle/adddialog.ui @@ -0,0 +1,113 @@ + + AddDialog + + + + 0 + 0 + 565 + 113 + + + + + 0 + 0 + + + + + + + + 0 + 0 + + + + Files/Folders to Compress + + + + + + + 0 + 0 + + + + false + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::NoSelection + + + + 32 + 32 + + + + Qt::ElideMiddle + + + QAbstractItemView::ScrollPerPixel + + + QListView::Adjust + + + QListView::IconMode + + + true + + + + + + + + + + + 0 + 0 + + + + Extra Compression Options + + + + + + Easter egg for the developers: +This is where future versions will have extra compression options for the various compression interfaces. + + + true + + + + + + + + + + + diff --git a/ark/kerfuffle/addtoarchive.cpp b/ark/kerfuffle/addtoarchive.cpp new file mode 100644 index 00000000..d13e0958 --- /dev/null +++ b/ark/kerfuffle/addtoarchive.cpp @@ -0,0 +1,213 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2008 Harald Hvaal + * Copyright (C) 2009 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "addtoarchive.h" +#include "adddialog.h" +#include "archive.h" +#include "jobs.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Kerfuffle +{ +AddToArchive::AddToArchive(QObject *parent) + : KJob(parent), m_changeToFirstPath(false) +{ +} + +AddToArchive::~AddToArchive() +{ +} + +void AddToArchive::setAutoFilenameSuffix(const QString& suffix) +{ + m_autoFilenameSuffix = suffix; +} + +void AddToArchive::setChangeToFirstPath(bool value) +{ + m_changeToFirstPath = value; +} + +void AddToArchive::setFilename(const KUrl& path) +{ + m_filename = path.pathOrUrl(); +} + +void AddToArchive::setMimeType(const QString & mimeType) +{ + m_mimeType = mimeType; +} + +bool AddToArchive::showAddDialog(void) +{ + QWeakPointer dialog = new Kerfuffle::AddDialog( + m_inputs, // itemsToAdd + KUrl(m_firstPath), // startDir + QLatin1String( "" ), // filter + NULL, // parent + NULL); // widget + + bool ret = dialog.data()->exec(); + + if (ret) { + kDebug() << "Returned URL:" << dialog.data()->selectedUrl(); + kDebug() << "Returned mime:" << dialog.data()->currentMimeFilter(); + setFilename(dialog.data()->selectedUrl()); + setMimeType(dialog.data()->currentMimeFilter()); + } + + delete dialog.data(); + + return ret; +} + +bool AddToArchive::addInput(const KUrl& url) +{ + m_inputs << url.pathOrUrl( + QFileInfo(url.pathOrUrl()).isDir() ? + KUrl::AddTrailingSlash : + KUrl::RemoveTrailingSlash + ); + + if (m_firstPath.isEmpty()) { + QString firstEntry = url.pathOrUrl(KUrl::RemoveTrailingSlash); + m_firstPath = QFileInfo(firstEntry).dir().absolutePath(); + } + + return true; +} + +void AddToArchive::start() +{ + QTimer::singleShot(0, this, SLOT(slotStartJob())); +} + +// TODO: If this class should ever be called outside main.cpp, +// the returns should be preceded by emitResult(). +void AddToArchive::slotStartJob(void) +{ + kDebug(); + + Kerfuffle::CompressionOptions options; + + if (!m_inputs.size()) { + KMessageBox::error(NULL, i18n("No input files were given.")); + return; + } + + Kerfuffle::Archive *archive; + if (!m_filename.isEmpty()) { + archive = Kerfuffle::Archive::create(m_filename, m_mimeType, this); + kDebug() << "Set filename to " << m_filename; + } else { + if (m_autoFilenameSuffix.isEmpty()) { + KMessageBox::error(NULL, i18n("You need to either supply a filename for the archive or a suffix (such as rar, tar.gz) with the --autofilename argument.")); + return; + } + + if (m_firstPath.isEmpty()) { + kDebug() << "Weird, this should not happen. no firstpath defined. aborting"; + return; + } + + QString base = QFileInfo(m_inputs.first()).absoluteFilePath(); + if (base.endsWith(QLatin1Char('/'))) { + base.chop(1); + } + + QString finalName = base + QLatin1Char( '.' ) + m_autoFilenameSuffix; + + //if file already exists, append a number to the base until it doesn't + //exist + int appendNumber = 0; + while (QFileInfo(finalName).exists()) { + ++appendNumber; + finalName = base + QLatin1Char( '_' ) + QString::number(appendNumber) + QLatin1Char( '.' ) + m_autoFilenameSuffix; + } + + kDebug() << "Autoset filename to "<< finalName; + archive = Kerfuffle::Archive::create(finalName, m_mimeType, this); + } + + if (archive == NULL) { + KMessageBox::error(NULL, i18n("Failed to create the new archive. Permissions might not be sufficient.")); + return; + } else if (archive->isReadOnly()) { + KMessageBox::error(NULL, i18n("It is not possible to create archives of this type.")); + return; + } + + if (m_changeToFirstPath) { + if (m_firstPath.isEmpty()) { + kDebug() << "Weird, this should not happen. no firstpath defined. aborting"; + return; + } + + const QDir stripDir(m_firstPath); + + for (int i = 0; i < m_inputs.size(); ++i) { + m_inputs[i] = stripDir.absoluteFilePath(m_inputs.at(i)); + } + + options[QLatin1String( "GlobalWorkDir" )] = stripDir.path(); + kDebug() << "Setting GlobalWorkDir to " << stripDir.path(); + } + + Kerfuffle::AddJob *job = + archive->addFiles(m_inputs, options); + + KIO::getJobTracker()->registerJob(job); + + connect(job, SIGNAL(result(KJob*)), + this, SLOT(slotFinished(KJob*))); + + job->start(); +} + +void AddToArchive::slotFinished(KJob *job) +{ + kDebug(); + + if (job->error()) { + KMessageBox::error(NULL, job->errorText()); + } + + emitResult(); +} +} diff --git a/ark/kerfuffle/addtoarchive.h b/ark/kerfuffle/addtoarchive.h new file mode 100644 index 00000000..fa4608e0 --- /dev/null +++ b/ark/kerfuffle/addtoarchive.h @@ -0,0 +1,85 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2008 Harald Hvaal + * Copyright (C) 2009 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ADDTOARCHIVE_H +#define ADDTOARCHIVE_H + +#include "kerfuffle_export.h" + +#include +#include + +/** + * Compresses all input files into an archive. + * + * This is a job class that creates a compressed archive + * with all the given input files. + * + * It provides the functionality for the --add command-line + * option, and does not need the GUI to be running. + * + * @author Harald Hvaal + */ +namespace Kerfuffle +{ +class KERFUFFLE_EXPORT AddToArchive : public KJob +{ + Q_OBJECT + +public: + AddToArchive(QObject *parent = 0); + ~AddToArchive(); + + bool showAddDialog(); + void setPreservePaths(bool value); + void setChangeToFirstPath(bool value); + +public slots: + bool addInput(const KUrl& url); + void setAutoFilenameSuffix(const QString& suffix); + void setFilename(const KUrl& path); + void setMimeType(const QString & mimeType); + void start(); + +private slots: + void slotFinished(KJob*); + void slotStartJob(); + +private: + QString m_filename; + QString m_strippedPath; + QString m_autoFilenameSuffix; + QString m_firstPath; + QString m_mimeType; + QStringList m_inputs; + bool m_changeToFirstPath; +}; +} + +#endif // ADDTOARCHIVE_H + diff --git a/ark/kerfuffle/archive.cpp b/ark/kerfuffle/archive.cpp new file mode 100644 index 00000000..c2c5b185 --- /dev/null +++ b/ark/kerfuffle/archive.cpp @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2007 Henrique Pinto + * Copyright (c) 2008 Harald Hvaal + * Copyright (c) 2009-2011 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive.h" +#include "archiveinterface.h" +#include "jobs.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static bool comparePlugins(const KService::Ptr &p1, const KService::Ptr &p2) +{ + return (p1->property(QLatin1String( "X-KDE-Priority" )).toInt()) > (p2->property(QLatin1String( "X-KDE-Priority" )).toInt()); +} + +static QString determineMimeType(const QString& filename) +{ + if (!QFile::exists(filename)) { + return KMimeType::findByPath(filename)->name(); + } + + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + return QString(); + } + + const qint64 maxSize = 0x100000; // 1MB + const qint64 bufferSize = qMin(maxSize, file.size()); + const QByteArray buffer = file.read(bufferSize); + + return KMimeType::findByNameAndContent(filename, buffer)->name(); +} + +static KService::List findPluginOffers(const QString& filename, const QString& fixedMimeType) +{ + KService::List offers; + + const QString mimeType = fixedMimeType.isEmpty() ? determineMimeType(filename) : fixedMimeType; + + if (!mimeType.isEmpty()) { + offers = KMimeTypeTrader::self()->query(mimeType, QLatin1String( "Kerfuffle/Plugin" ), QLatin1String( "(exist Library)" )); + qSort(offers.begin(), offers.end(), comparePlugins); + } + + return offers; +} + +namespace Kerfuffle +{ + +Archive *Archive::create(const QString &fileName, QObject *parent) +{ + return create(fileName, QString(), parent); +} + +Archive *Archive::create(const QString &fileName, const QString &fixedMimeType, QObject *parent) +{ + qRegisterMetaType("ArchiveEntry"); + + const KService::List offers = findPluginOffers(fileName, fixedMimeType); + + if (offers.isEmpty()) { + kDebug() << "Could not find a plugin to handle" << fileName; + return NULL; + } + + const QString pluginName = offers.first()->library(); + kDebug() << "Loading plugin" << pluginName; + + KPluginFactory * const factory = KPluginLoader(pluginName).factory(); + if (!factory) { + kDebug() << "Invalid plugin factory for" << pluginName; + return NULL; + } + + QVariantList args; + args.append(QVariant(QFileInfo(fileName).absoluteFilePath())); + + ReadOnlyArchiveInterface * const iface = factory->create(0, args); + if (!iface) { + kDebug() << "Could not create plugin instance" << pluginName << "for" << fileName; + return NULL; + } + + return new Archive(iface, parent); +} + +Archive::Archive(ReadOnlyArchiveInterface *archiveInterface, QObject *parent) + : QObject(parent), + m_iface(archiveInterface), + m_hasBeenListed(false), + m_isPasswordProtected(false), + m_isSingleFolderArchive(false) +{ + Q_ASSERT(archiveInterface); + archiveInterface->setParent(this); +} + +Archive::~Archive() +{ +} + +bool Archive::isReadOnly() const +{ + return m_iface->isReadOnly(); +} + +KJob* Archive::open() +{ + return 0; +} + +KJob* Archive::create() +{ + return 0; +} + +ListJob* Archive::list() +{ + ListJob *job = new ListJob(m_iface, this); + job->setAutoDelete(false); + + //if this job has not been listed before, we grab the opportunity to + //collect some information about the archive + if (!m_hasBeenListed) { + connect(job, SIGNAL(result(KJob*)), + this, SLOT(onListFinished(KJob*))); + } + return job; +} + +DeleteJob* Archive::deleteFiles(const QList & files) +{ + if (m_iface->isReadOnly()) { + return 0; + } + DeleteJob *newJob = new DeleteJob(files, static_cast(m_iface), this); + + return newJob; +} + +AddJob* Archive::addFiles(const QStringList & files, const CompressionOptions& options) +{ + Q_ASSERT(!m_iface->isReadOnly()); + AddJob *newJob = new AddJob(files, options, static_cast(m_iface), this); + connect(newJob, SIGNAL(result(KJob*)), + this, SLOT(onAddFinished(KJob*))); + return newJob; +} + +ExtractJob* Archive::copyFiles(const QList & files, const QString & destinationDir, ExtractionOptions options) +{ + ExtractionOptions newOptions = options; + if (isPasswordProtected()) { + newOptions[QLatin1String( "PasswordProtectedHint" )] = true; + } + + ExtractJob *newJob = new ExtractJob(files, destinationDir, newOptions, m_iface, this); + return newJob; +} + +QString Archive::fileName() const +{ + return m_iface->filename(); +} + +void Archive::onAddFinished(KJob* job) +{ + //if the archive was previously a single folder archive and an add job + //has successfully finished, then it is no longer a single folder + //archive (for the current implementation, which does not allow adding + //folders/files other places than the root. + //TODO: handle the case of creating a new file and singlefolderarchive + //then. + if (m_isSingleFolderArchive && !job->error()) { + m_isSingleFolderArchive = false; + } +} + +void Archive::onListFinished(KJob* job) +{ + ListJob *ljob = qobject_cast(job); + m_extractedFilesSize = ljob->extractedFilesSize(); + m_isSingleFolderArchive = ljob->isSingleFolderArchive(); + m_isPasswordProtected = ljob->isPasswordProtected(); + m_subfolderName = ljob->subfolderName(); + if (m_subfolderName.isEmpty()) { + QFileInfo fi(fileName()); + QString base = fi.completeBaseName(); + + //special case for tar.gz/bzip2 files + if (base.right(4).toUpper() == QLatin1String(".TAR")) { + base.chop(4); + } + + m_subfolderName = base; + } + + m_hasBeenListed = true; +} + +void Archive::listIfNotListed() +{ + if (!m_hasBeenListed) { + KJob *job = list(); + + connect(job, SIGNAL(userQuery(Kerfuffle::Query*)), + SLOT(onUserQuery(Kerfuffle::Query*))); + + QEventLoop loop(this); + + connect(job, SIGNAL(result(KJob*)), + &loop, SLOT(quit())); + job->start(); + loop.exec(); // krazy:exclude=crashy + } +} + +void Archive::onUserQuery(Query* query) +{ + query->execute(); +} + +bool Archive::isSingleFolderArchive() +{ + listIfNotListed(); + return m_isSingleFolderArchive; +} + +bool Archive::isPasswordProtected() +{ + listIfNotListed(); + return m_isPasswordProtected; +} + +QString Archive::subfolderName() +{ + listIfNotListed(); + return m_subfolderName; +} + +void Archive::setPassword(const QString &password) +{ + m_iface->setPassword(password); +} + +QStringList supportedMimeTypes() +{ + const QLatin1String constraint("(exist Library)"); + const QLatin1String basePartService("Kerfuffle/Plugin"); + + const KService::List offers = KServiceTypeTrader::self()->query(basePartService, constraint); + KService::List::ConstIterator it = offers.constBegin(); + KService::List::ConstIterator itEnd = offers.constEnd(); + + QStringList supported; + + for (; it != itEnd; ++it) { + KService::Ptr service = *it; + QStringList mimeTypes = service->serviceTypes(); + + foreach (const QString& mimeType, mimeTypes) { + if (mimeType != basePartService && !supported.contains(mimeType)) { + supported.append(mimeType); + } + } + } + + kDebug() << "Returning" << supported; + + return supported; +} + +QStringList supportedWriteMimeTypes() +{ + const QLatin1String constraint("(exist Library) and ([X-KDE-Kerfuffle-ReadWrite] == true)"); + const QLatin1String basePartService("Kerfuffle/Plugin"); + + const KService::List offers = KServiceTypeTrader::self()->query(basePartService, constraint); + KService::List::ConstIterator it = offers.constBegin(); + KService::List::ConstIterator itEnd = offers.constEnd(); + + QStringList supported; + + for (; it != itEnd; ++it) { + KService::Ptr service = *it; + QStringList mimeTypes = service->serviceTypes(); + + foreach (const QString& mimeType, mimeTypes) { + if (mimeType != basePartService && !supported.contains(mimeType)) { + supported.append(mimeType); + } + } + } + + kDebug() << "Returning" << supported; + + return supported; +} + +} // namespace Kerfuffle diff --git a/ark/kerfuffle/archive.h b/ark/kerfuffle/archive.h new file mode 100644 index 00000000..515788f9 --- /dev/null +++ b/ark/kerfuffle/archive.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2007 Henrique Pinto + * Copyright (c) 2008 Harald Hvaal + * Copyright (c) 2011 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ARCHIVE_H +#define ARCHIVE_H + +#include "kerfuffle_export.h" + +#include +#include +#include + +class KJob; + +namespace Kerfuffle +{ +class ListJob; +class ExtractJob; +class DeleteJob; +class AddJob; +class Query; +class ReadOnlyArchiveInterface; + +/** + * Meta data related to one entry in a compressed archive. + * + * When creating a plugin, information about every single entry in + * an archive is contained in an ArchiveEntry, and metadata + * is set with the entries in this enum. + * + * Please notice that not all archive formats support all the properties + * below, so set those that are available. + */ +enum EntryMetaDataType { + FileName = 0, /**< The entry's file name */ + InternalID, /**< The entry's ID for Ark's internal manipulation */ + Permissions, /**< The entry's permissions */ + Owner, /**< The user the entry belongs to */ + Group, /**< The user group the entry belongs to */ + Size, /**< The entry's original size */ + CompressedSize, /**< The compressed size for the entry */ + Link, /**< The entry is a symbolic link */ + Ratio, /**< The compression ratio for the entry */ + CRC, /**< The entry's CRC */ + Method, /**< The compression method used on the entry */ + Version, /**< The archiver version needed to extract the entry */ + Timestamp, /**< The timestamp for the current entry */ + IsDirectory, /**< The entry is a directory */ + Comment, + IsPasswordProtected, /**< The entry is password-protected */ + Custom = 1048576 +}; + +typedef QHash ArchiveEntry; + +/** +These are the extra options for doing the compression. Naming convention +is CamelCase with either Global, or the compression type (such as Zip, +Rar, etc), followed by the property name used + */ +typedef QHash CompressionOptions; +typedef QHash ExtractionOptions; + +class KERFUFFLE_EXPORT Archive : public QObject +{ + Q_OBJECT + +public: + static Archive *create(const QString &fileName, QObject *parent = 0); + static Archive *create(const QString &fileName, const QString &fixedMimeType, QObject *parent = 0); + ~Archive(); + + QString fileName() const; + bool isReadOnly() const; + + KJob* open(); + KJob* create(); + ListJob* list(); + DeleteJob* deleteFiles(const QList & files); + + /** + * Compression options that should be handled by all interfaces: + * + * GlobalWorkDir - Change to this dir before adding the new files. + * The path names should then be added relative to this directory. + * + * TODO: find a way to actually add files to specific locations in + * the archive + * (not supported yet) GlobalPathInArchive - a path relative to the + * archive root where the files will be added under + * + */ + AddJob* addFiles(const QStringList & files, const CompressionOptions& options = CompressionOptions()); + + ExtractJob* copyFiles(const QList & files, const QString & destinationDir, ExtractionOptions options = ExtractionOptions()); + + bool isSingleFolderArchive(); + QString subfolderName(); + bool isPasswordProtected(); + + void setPassword(const QString &password); + +private slots: + void onListFinished(KJob*); + void onAddFinished(KJob*); + void onUserQuery(Kerfuffle::Query*); + +private: + Archive(ReadOnlyArchiveInterface *archiveInterface, QObject *parent = 0); + + void listIfNotListed(); + ReadOnlyArchiveInterface *m_iface; + bool m_hasBeenListed; + bool m_isPasswordProtected; + bool m_isSingleFolderArchive; + QString m_subfolderName; + qlonglong m_extractedFilesSize; +}; + +KERFUFFLE_EXPORT QStringList supportedMimeTypes(); +KERFUFFLE_EXPORT QStringList supportedWriteMimeTypes(); +} // namespace Kerfuffle + + +#endif // ARCHIVE_H diff --git a/ark/kerfuffle/archiveinterface.cpp b/ark/kerfuffle/archiveinterface.cpp new file mode 100644 index 00000000..3319de07 --- /dev/null +++ b/ark/kerfuffle/archiveinterface.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2007 Henrique Pinto + * Copyright (c) 2008-2009 Harald Hvaal + * Copyright (c) 2009-2012 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archiveinterface.h" +#include +#include + +#include +#include + +namespace Kerfuffle +{ +ReadOnlyArchiveInterface::ReadOnlyArchiveInterface(QObject *parent, const QVariantList & args) + : QObject(parent), m_waitForFinishedSignal(false) +{ + kDebug(); + m_filename = args.first().toString(); +} + +ReadOnlyArchiveInterface::~ReadOnlyArchiveInterface() +{ +} + +QString ReadOnlyArchiveInterface::filename() const +{ + return m_filename; +} + +bool ReadOnlyArchiveInterface::isReadOnly() const +{ + return true; +} + +bool ReadOnlyArchiveInterface::open() +{ + return true; +} + +void ReadOnlyArchiveInterface::setPassword(const QString &password) +{ + m_password = password; +} + +QString ReadOnlyArchiveInterface::password() const +{ + return m_password; +} + +bool ReadOnlyArchiveInterface::doKill() +{ + //default implementation + return false; +} + +bool ReadOnlyArchiveInterface::doSuspend() +{ + //default implementation + return false; +} + +bool ReadOnlyArchiveInterface::doResume() +{ + //default implementation + return false; +} + +ReadWriteArchiveInterface::ReadWriteArchiveInterface(QObject *parent, const QVariantList & args) + : ReadOnlyArchiveInterface(parent, args) +{ +} + +ReadWriteArchiveInterface::~ReadWriteArchiveInterface() +{ +} + +bool ReadOnlyArchiveInterface::waitForFinishedSignal() +{ + return m_waitForFinishedSignal; +} + +void ReadOnlyArchiveInterface::setWaitForFinishedSignal(bool value) +{ + m_waitForFinishedSignal = value; +} + +bool ReadWriteArchiveInterface::isReadOnly() const +{ + QFileInfo fileInfo(filename()); + if (fileInfo.exists()) { + return ! fileInfo.isWritable(); + } else { + return !fileInfo.dir().exists(); // TODO: Should also check if we can create a file in that directory + } +} + +} // namespace Kerfuffle + +#include "archiveinterface.moc" diff --git a/ark/kerfuffle/archiveinterface.h b/ark/kerfuffle/archiveinterface.h new file mode 100644 index 00000000..801318b7 --- /dev/null +++ b/ark/kerfuffle/archiveinterface.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2007 Henrique Pinto + * Copyright (c) 2008-2009 Harald Hvaal + * Copyright (c) 2009-2012 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ARCHIVEINTERFACE_H +#define ARCHIVEINTERFACE_H + +#include "archive.h" +#include "kerfuffle_export.h" + +#include +#include +#include +#include + +namespace Kerfuffle +{ +class Query; + +class KERFUFFLE_EXPORT ReadOnlyArchiveInterface: public QObject +{ + Q_OBJECT +public: + explicit ReadOnlyArchiveInterface(QObject *parent, const QVariantList & args); + virtual ~ReadOnlyArchiveInterface(); + + /** + * Returns the filename of the archive currently being handled. + */ + QString filename() const; + + /** + * Returns whether the file can only be read. + * + * @return @c true The file cannot be written. + * @return @c false The file can be read and written. + */ + virtual bool isReadOnly() const; + + virtual bool open(); + + /** + * List archive contents. + * This runs the process of reading archive contents. + * When subclassing, you can block as long as you need, the function runs + * in its own thread. + * @returns whether the listing succeeded. + * @note If returning false, make sure to emit the error() signal beforewards to notify + * the user of the error condition. + */ + virtual bool list() = 0; + void setPassword(const QString &password); + + /** + * Extract files from archive. + * Globally recognized extraction options: + * @li PreservePaths - preserve file paths (extract flat if false) + * @li RootNode - node in the archive which will correspond to the @arg destinationDirectory + * When subclassing, you can block as long as you need, the function runs + * in its own thread. + * @returns whether the listing succeeded. + * @note If returning false, make sure to emit the error() signal beforewards to notify + * the user of the error condition. + */ + virtual bool copyFiles(const QList & files, const QString & destinationDirectory, ExtractionOptions options) = 0; + + bool waitForFinishedSignal(); + + virtual bool doKill(); + virtual bool doSuspend(); + virtual bool doResume(); + +signals: + void error(const QString &message, const QString &details = QString()); + void entry(const ArchiveEntry &archiveEntry); + void entryRemoved(const QString &path); + void progress(double progress); + void info(const QString &info); + void finished(bool result); + void userQuery(Query *query); + +protected: + QString password() const; + /** + * Setting this option to true will not exit the thread with the + * exit of the various functions, but rather when finished(bool) is + * called. Doing this one can use the event loop easily while doing + * the operation. + */ + void setWaitForFinishedSignal(bool value); + +private: + QString m_filename; + QString m_password; + bool m_waitForFinishedSignal; +}; + +class KERFUFFLE_EXPORT ReadWriteArchiveInterface: public ReadOnlyArchiveInterface +{ + Q_OBJECT +public: + explicit ReadWriteArchiveInterface(QObject *parent, const QVariantList & args); + virtual ~ReadWriteArchiveInterface(); + + virtual bool isReadOnly() const; + + //see archive.h for a list of what the compressionoptions might + //contain + virtual bool addFiles(const QStringList & files, const CompressionOptions& options) = 0; + virtual bool deleteFiles(const QList & files) = 0; +}; + +} // namespace Kerfuffle + +#endif // ARCHIVEINTERFACE_H diff --git a/ark/kerfuffle/ark.kcfg b/ark/kerfuffle/ark.kcfg new file mode 100644 index 00000000..97d20866 --- /dev/null +++ b/ark/kerfuffle/ark.kcfg @@ -0,0 +1,25 @@ + + + + + + + false + + + + false + + + + true + + + + + + + diff --git a/ark/kerfuffle/cliinterface.cpp b/ark/kerfuffle/cliinterface.cpp new file mode 100644 index 00000000..eab1c6a3 --- /dev/null +++ b/ark/kerfuffle/cliinterface.cpp @@ -0,0 +1,751 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2009 Harald Hvaal + * Copyright (C) 2009-2011 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cliinterface.h" +#include "queries.h" + +#ifdef Q_OS_WIN +# include +#else +# include +# include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kerfuffle +{ +CliInterface::CliInterface(QObject *parent, const QVariantList & args) + : ReadWriteArchiveInterface(parent, args), + m_process(0), + m_listEmptyLines(false), + m_abortingOperation(false) +{ + //because this interface uses the event loop + setWaitForFinishedSignal(true); + + if (QMetaType::type("QProcess::ExitStatus") == 0) { + qRegisterMetaType("QProcess::ExitStatus"); + } +} + +void CliInterface::cacheParameterList() +{ + m_param = parameterList(); + Q_ASSERT(m_param.contains(ExtractProgram)); + Q_ASSERT(m_param.contains(ListProgram)); + Q_ASSERT(m_param.contains(PreservePathSwitch)); + Q_ASSERT(m_param.contains(FileExistsExpression)); + Q_ASSERT(m_param.contains(FileExistsInput)); +} + +CliInterface::~CliInterface() +{ + Q_ASSERT(!m_process); +} + +void CliInterface::setListEmptyLines(bool emptyLines) +{ + m_listEmptyLines = emptyLines; +} + +bool CliInterface::list() +{ + cacheParameterList(); + m_operationMode = List; + + QStringList args = m_param.value(ListArgs).toStringList(); + substituteListVariables(args); + + if (!runProcess(m_param.value(ListProgram).toStringList(), args)) { + failOperation(); + return false; + } + + return true; +} + +bool CliInterface::copyFiles(const QList & files, const QString & destinationDirectory, ExtractionOptions options) +{ + kDebug(); + cacheParameterList(); + + m_operationMode = Copy; + + //start preparing the argument list + QStringList args = m_param.value(ExtractArgs).toStringList(); + + //now replace the various elements in the list + for (int i = 0; i < args.size(); ++i) { + QString argument = args.at(i); + kDebug() << "Processing argument " << argument; + + if (argument == QLatin1String( "$Archive" )) { + args[i] = filename(); + } + + if (argument == QLatin1String( "$PreservePathSwitch" )) { + QStringList replacementFlags = m_param.value(PreservePathSwitch).toStringList(); + Q_ASSERT(replacementFlags.size() == 2); + + bool preservePaths = options.value(QLatin1String( "PreservePaths" )).toBool(); + QString theReplacement; + if (preservePaths) { + theReplacement = replacementFlags.at(0); + } else { + theReplacement = replacementFlags.at(1); + } + + if (theReplacement.isEmpty()) { + args.removeAt(i); + --i; //decrement to compensate for the variable we removed + } else { + //but in this case we don't have to decrement, we just + //replace it + args[i] = theReplacement; + } + } + + if (argument == QLatin1String( "$PasswordSwitch" )) { + //if the PasswordSwitch argument has been added, we at least + //assume that the format of the switch has been added as well + Q_ASSERT(m_param.contains(PasswordSwitch)); + + //we will decrement i afterwards + args.removeAt(i); + + //if we get a hint about this being a password protected archive, ask about + //the password in advance. + if ((options.value(QLatin1String("PasswordProtectedHint")).toBool()) && + (password().isEmpty())) { + kDebug() << "Password hint enabled, querying user"; + + Kerfuffle::PasswordNeededQuery query(filename()); + emit userQuery(&query); + query.waitForResponse(); + + if (query.responseCancelled()) { + failOperation(); + return false; + } + setPassword(query.password()); + } + + QString pass = password(); + + if (!pass.isEmpty()) { + QStringList theSwitch = m_param.value(PasswordSwitch).toStringList(); + for (int j = 0; j < theSwitch.size(); ++j) { + //get the argument part + QString newArg = theSwitch.at(j); + + //substitute the $Path + newArg.replace(QLatin1String( "$Password" ), pass); + + //put it in the arg list + args.insert(i + j, newArg); + ++i; + + } + } + --i; //decrement to compensate for the variable we replaced + } + + if (argument == QLatin1String( "$RootNodeSwitch" )) { + //if the RootNodeSwitch argument has been added, we at least + //assume that the format of the switch has been added as well + Q_ASSERT(m_param.contains(RootNodeSwitch)); + + //we will decrement i afterwards + args.removeAt(i); + + QString rootNode; + if (options.contains(QLatin1String( "RootNode" ))) { + rootNode = options.value(QLatin1String( "RootNode" )).toString(); + kDebug() << "Set root node " << rootNode; + } + + if (!rootNode.isEmpty()) { + QStringList theSwitch = m_param.value(RootNodeSwitch).toStringList(); + for (int j = 0; j < theSwitch.size(); ++j) { + //get the argument part + QString newArg = theSwitch.at(j); + + //substitute the $Path + newArg.replace(QLatin1String( "$Path" ), rootNode); + + //put it in the arg list + args.insert(i + j, newArg); + ++i; + + } + } + --i; //decrement to compensate for the variable we replaced + } + + if (argument == QLatin1String( "$Files" )) { + args.removeAt(i); + for (int j = 0; j < files.count(); ++j) { + args.insert(i + j, escapeFileName(files.at(j).toString())); + ++i; + } + --i; + } + } + + kDebug() << "Setting current dir to " << destinationDirectory; + QDir::setCurrent(destinationDirectory); + + if (!runProcess(m_param.value(ExtractProgram).toStringList(), args)) { + failOperation(); + return false; + } + + return true; +} + +bool CliInterface::addFiles(const QStringList & files, const CompressionOptions& options) +{ + cacheParameterList(); + + m_operationMode = Add; + + const QString globalWorkDir = options.value(QLatin1String( "GlobalWorkDir" )).toString(); + const QDir workDir = globalWorkDir.isEmpty() ? QDir::current() : QDir(globalWorkDir); + if (!globalWorkDir.isEmpty()) { + kDebug() << "GlobalWorkDir is set, changing dir to " << globalWorkDir; + QDir::setCurrent(globalWorkDir); + } + + //start preparing the argument list + QStringList args = m_param.value(AddArgs).toStringList(); + + //now replace the various elements in the list + for (int i = 0; i < args.size(); ++i) { + const QString argument = args.at(i); + kDebug() << "Processing argument " << argument; + + if (argument == QLatin1String( "$Archive" )) { + args[i] = filename(); + } + + if (argument == QLatin1String( "$Files" )) { + args.removeAt(i); + for (int j = 0; j < files.count(); ++j) { + // #191821: workDir must be used instead of QDir::current() + // so that symlinks aren't resolved automatically + // TODO: this kind of call should be moved upwards in the + // class hierarchy to avoid code duplication + const QString relativeName = + workDir.relativeFilePath(files.at(j)); + + args.insert(i + j, relativeName); + ++i; + } + --i; + } + } + + if (!runProcess(m_param.value(AddProgram).toStringList(), args)) { + failOperation(); + return false; + } + + return true; +} + +bool CliInterface::deleteFiles(const QList & files) +{ + cacheParameterList(); + m_operationMode = Delete; + + //start preparing the argument list + QStringList args = m_param.value(DeleteArgs).toStringList(); + + //now replace the various elements in the list + for (int i = 0; i < args.size(); ++i) { + QString argument = args.at(i); + kDebug() << "Processing argument " << argument; + + if (argument == QLatin1String( "$Archive" )) { + args[i] = filename(); + } else if (argument == QLatin1String( "$Files" )) { + args.removeAt(i); + for (int j = 0; j < files.count(); ++j) { + args.insert(i + j, escapeFileName(files.at(j).toString())); + ++i; + } + --i; + } + } + + m_removedFiles = files; + + if (!runProcess(m_param.value(DeleteProgram).toStringList(), args)) { + failOperation(); + return false; + } + + return true; +} + +bool CliInterface::runProcess(const QStringList& programNames, const QStringList& arguments) +{ + QString programPath; + for (int i = 0; i < programNames.count(); i++) { + programPath = KStandardDirs::findExe(programNames.at(i)); + if (!programPath.isEmpty()) + break; + } + if (programPath.isEmpty()) { + const QString names = programNames.join(QLatin1String(", ")); + emit error(i18ncp("@info", "Failed to locate program %2 on disk.", + "Failed to locate programs %2 on disk.", programNames.count(), names)); + emit finished(false); + return false; + } + + kDebug() << "Executing" << programPath << arguments; + + if (m_process) { + m_process->waitForFinished(); + delete m_process; + } + +#ifdef Q_OS_WIN + m_process = new KProcess; +#else + m_process = new KPtyProcess; + m_process->setPtyChannels(KPtyProcess::StdinChannel); + QEventLoop loop; + connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit()), Qt::DirectConnection); +#endif + + m_process->setOutputChannelMode(KProcess::MergedChannels); + m_process->setNextOpenMode(QIODevice::ReadWrite | QIODevice::Unbuffered | QIODevice::Text); + m_process->setProgram(programPath, arguments); + + connect(m_process, SIGNAL(readyReadStandardOutput()), SLOT(readStdout()), Qt::DirectConnection); + connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(processFinished(int,QProcess::ExitStatus)), Qt::DirectConnection); + + m_stdOutData.clear(); + + m_process->start(); + +#ifdef Q_OS_WIN + bool ret = m_process->waitForFinished(-1); +#else + bool ret = (loop.exec(QEventLoop::WaitForMoreEvents | QEventLoop::ExcludeUserInputEvents) == 0); +#endif + + Q_ASSERT(!m_process); + + return ret; +} + +void CliInterface::processFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + kDebug() << exitCode << exitStatus; + + //if the m_process pointer is gone, then there is nothing to worry + //about here + if (!m_process) { + return; + } + + if (m_operationMode == Delete) { + foreach(const QVariant& v, m_removedFiles) { + emit entryRemoved(v.toString()); + } + } + + //handle all the remaining data in the process + readStdout(true); + + delete m_process; + m_process = 0; + + emit progress(1.0); + + if (m_operationMode == Add) { + list(); + return; + } + + //and we're finished + emit finished(true); +} + +void CliInterface::failOperation() +{ + // TODO: Would be good to unit test #304764/#304178. + kDebug(); + doKill(); +} + +void CliInterface::readStdout(bool handleAll) +{ + //when hacking this function, please remember the following: + //- standard output comes in unpredictable chunks, this is why + //you can never know if the last part of the output is a complete line or not + //- console applications are not really consistent about what + //characters they send out (newline, backspace, carriage return, + //etc), so keep in mind that this function is supposed to handle + //all those special cases and be the lowest common denominator + + if (m_abortingOperation) + return; + + Q_ASSERT(m_process); + + if (!m_process->bytesAvailable()) { + //if process has no more data, we can just bail out + return; + } + + //if the process is still not finished (m_process is appearantly not + //set to NULL if here), then the operation should definitely not be in + //the main thread as this would freeze everything. assert this. + Q_ASSERT(QThread::currentThread() != QApplication::instance()->thread()); + + QByteArray dd = m_process->readAllStandardOutput(); + m_stdOutData += dd; + + QList lines = m_stdOutData.split('\n'); + + //The reason for this check is that archivers often do not end + //queries (such as file exists, wrong password) on a new line, but + //freeze waiting for input. So we check for errors on the last line in + //all cases. + // TODO: QLatin1String() might not be the best choice here. + // The call to handleLine() at the end of the method uses + // QString::fromLocal8Bit(), for example. + // TODO: The same check methods are called in handleLine(), this + // is suboptimal. + bool foundErrorMessage = + (checkForErrorMessage(QLatin1String( lines.last() ), WrongPasswordPatterns) || + checkForErrorMessage(QLatin1String( lines.last() ), ExtractionFailedPatterns) || + checkForPasswordPromptMessage(QLatin1String(lines.last())) || + checkForFileExistsMessage(QLatin1String( lines.last() ))); + + if (foundErrorMessage) { + handleAll = true; + } + + //this is complex, here's an explanation: + //if there is no newline, then there is no guaranteed full line to + //handle in the output. The exception is that it is supposed to handle + //all the data, OR if there's been an error message found in the + //partial data. + if (lines.size() == 1 && !handleAll) { + return; + } + + if (handleAll) { + m_stdOutData.clear(); + } else { + //because the last line might be incomplete we leave it for now + //note, this last line may be an empty string if the stdoutdata ends + //with a newline + m_stdOutData = lines.takeLast(); + } + + foreach(const QByteArray& line, lines) { + if (!line.isEmpty() || (m_listEmptyLines && m_operationMode == List)) { + handleLine(QString::fromLocal8Bit(line)); + } + } +} + +void CliInterface::handleLine(const QString& line) +{ + // TODO: This should be implemented by each plugin; the way progress is + // shown by each CLI application is subject to a lot of variation. + if ((m_operationMode == Copy || m_operationMode == Add) && m_param.contains(CaptureProgress) && m_param.value(CaptureProgress).toBool()) { + //read the percentage + int pos = line.indexOf(QLatin1Char( '%' )); + if (pos != -1 && pos > 1) { + int percentage = line.mid(pos - 2, 2).toInt(); + emit progress(float(percentage) / 100); + return; + } + } + + if (m_operationMode == Copy) { + if (checkForPasswordPromptMessage(line)) { + kDebug() << "Found a password prompt"; + + Kerfuffle::PasswordNeededQuery query(filename()); + emit userQuery(&query); + query.waitForResponse(); + + if (query.responseCancelled()) { + failOperation(); + return; + } + + setPassword(query.password()); + + const QString response(password() + QLatin1Char('\n')); + writeToProcess(response.toLocal8Bit()); + + return; + } + + if (checkForErrorMessage(line, WrongPasswordPatterns)) { + kDebug() << "Wrong password!"; + emit error(i18n("Incorrect password.")); + failOperation(); + return; + } + + if (checkForErrorMessage(line, ExtractionFailedPatterns)) { + kDebug() << "Error in extraction!!"; + emit error(i18n("Extraction failed because of an unexpected error.")); + failOperation(); + return; + } + + if (handleFileExistsMessage(line)) { + return; + } + } + + if (m_operationMode == List) { + if (checkForPasswordPromptMessage(line)) { + kDebug() << "Found a password prompt"; + + Kerfuffle::PasswordNeededQuery query(filename()); + emit userQuery(&query); + query.waitForResponse(); + + if (query.responseCancelled()) { + failOperation(); + return; + } + + setPassword(query.password()); + + const QString response(password() + QLatin1Char('\n')); + writeToProcess(response.toLocal8Bit()); + + return; + } + + if (checkForErrorMessage(line, WrongPasswordPatterns)) { + kDebug() << "Wrong password!"; + emit error(i18n("Incorrect password.")); + failOperation(); + return; + } + + if (checkForErrorMessage(line, ExtractionFailedPatterns)) { + kDebug() << "Error in extraction!!"; + emit error(i18n("Extraction failed because of an unexpected error.")); + failOperation(); + return; + } + + if (handleFileExistsMessage(line)) { + return; + } + + readListLine(line); + return; + } +} + +bool CliInterface::checkForPasswordPromptMessage(const QString& line) +{ + const QString passwordPromptPattern(m_param.value(PasswordPromptPattern).toString()); + + if (passwordPromptPattern.isEmpty()) + return false; + + if (m_passwordPromptPattern.isEmpty()) { + m_passwordPromptPattern.setPattern(m_param.value(PasswordPromptPattern).toString()); + } + + if (m_passwordPromptPattern.indexIn(line) != -1) { + return true; + } + + return false; +} + +bool CliInterface::checkForFileExistsMessage(const QString& line) +{ + if (m_existsPattern.isEmpty()) { + m_existsPattern.setPattern(m_param.value(FileExistsExpression).toString()); + } + if (m_existsPattern.indexIn(line) != -1) { + kDebug() << "Detected file existing!! Filename " << m_existsPattern.cap(1); + return true; + } + + return false; +} + +bool CliInterface::handleFileExistsMessage(const QString& line) +{ + if (!checkForFileExistsMessage(line)) { + return false; + } + + const QString filename = m_existsPattern.cap(1); + + Kerfuffle::OverwriteQuery query(QDir::current().path() + QLatin1Char( '/' ) + filename); + query.setNoRenameMode(true); + emit userQuery(&query); + kDebug() << "Waiting response"; + query.waitForResponse(); + + kDebug() << "Finished response"; + + QString responseToProcess; + const QStringList choices = m_param.value(FileExistsInput).toStringList(); + + if (query.responseOverwrite()) { + responseToProcess = choices.at(0); + } else if (query.responseSkip()) { + responseToProcess = choices.at(1); + } else if (query.responseOverwriteAll()) { + responseToProcess = choices.at(2); + } else if (query.responseAutoSkip()) { + responseToProcess = choices.at(3); + } else if (query.responseCancelled()) { + if (choices.count() < 5) { // If the program has no way to cancel the extraction, we resort to killing it + return doKill(); + } + responseToProcess = choices.at(4); + } + + Q_ASSERT(!responseToProcess.isEmpty()); + + responseToProcess += QLatin1Char( '\n' ); + + writeToProcess(responseToProcess.toLocal8Bit()); + + return true; +} + +bool CliInterface::checkForErrorMessage(const QString& line, int parameterIndex) +{ + QList patterns; + + if (m_patternCache.contains(parameterIndex)) { + patterns = m_patternCache.value(parameterIndex); + } else { + if (!m_param.contains(parameterIndex)) { + return false; + } + + foreach(const QString& rawPattern, m_param.value(parameterIndex).toStringList()) { + patterns << QRegExp(rawPattern); + } + m_patternCache[parameterIndex] = patterns; + } + + foreach(const QRegExp& pattern, patterns) { + if (pattern.indexIn(line) != -1) { + return true; + } + } + return false; +} + +bool CliInterface::doKill() +{ + if (m_process) { + // Give some time for the application to finish gracefully + m_abortingOperation = true; + if (!m_process->waitForFinished(5)) { + m_process->kill(); + } + m_abortingOperation = false; + + return true; + } + + return false; +} + +bool CliInterface::doSuspend() +{ + return false; +} + +bool CliInterface::doResume() +{ + return false; +} + +void CliInterface::substituteListVariables(QStringList& params) +{ + for (int i = 0; i < params.size(); ++i) { + const QString parameter = params.at(i); + + if (parameter == QLatin1String( "$Archive" )) { + params[i] = filename(); + } + } +} + +QString CliInterface::escapeFileName(const QString& fileName) const +{ + return fileName; +} + +void CliInterface::writeToProcess(const QByteArray& data) +{ + Q_ASSERT(m_process); + Q_ASSERT(!data.isNull()); + + kDebug() << "Writing" << data << "to the process"; + +#ifdef Q_OS_WIN + m_process->write(data); +#else + m_process->pty()->write(data); +#endif +} + +} + +#include "cliinterface.moc" diff --git a/ark/kerfuffle/cliinterface.h b/ark/kerfuffle/cliinterface.h new file mode 100644 index 00000000..b4d79ed2 --- /dev/null +++ b/ark/kerfuffle/cliinterface.h @@ -0,0 +1,349 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2009 Harald Hvaal + * Copyright (C) 2009-2011 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef CLIINTERFACE_H +#define CLIINTERFACE_H + +#include "archiveinterface.h" +#include "kerfuffle_export.h" +#include +#include + +class KProcess; +class KPtyProcess; + +namespace Kerfuffle +{ + +enum CliInterfaceParameters { + + ///////////////[ COMMON ]///////////// + + /** + * Bool (default false) + * Will look for the %-sign in the stdout while working, in the form of + * (2%, 14%, 35%, etc etc), and report progress based upon this + */ + CaptureProgress = 0, + + /** + * QString + * Default: empty + * A regexp pattern that matches the program's password prompt. + */ + PasswordPromptPattern, + + ///////////////[ LIST ]///////////// + + /** + * QStringList + * The names to the program that will handle listing of this + * archive (eg "rar"). Will be searched for in PATH + */ + ListProgram, + /** + * QStringList + * The arguments that are passed to the program above for + * listing the archive. Special strings that will be + * substituted: + * $Archive - the path of the archive + */ + ListArgs, + + ///////////////[ EXTRACT ]///////////// + + /** + * QStringList + * The names to the program that will handle extracting of this + * archive (eg "rar"). Will be searched for in PATH + */ + ExtractProgram, + /** + * QStringList + * The arguments that are passed to the program above for + * extracting the archive. Special strings that will be + * substituted: + * $Archive - the path of the archive + * $Files - the files selected to be extracted, if any + * $PreservePathSwitch - the flag for extracting with full paths + * $RootNodeSwitch - the internal work dir in the archive (for example + * when the user has dragged a folder from the archive and wants it + * extracted relative to it) + * $PasswordSwitch - the switch setting the password. Note that this + * will not be inserted unless the listing function has emitted an + * entry with the IsPasswordProtected property set to true. + */ + ExtractArgs, + /** + * Bool (default false) + * When passing directories to the extract program, do not + * include trailing slashes + * e.g. if the user selected "foo/" and "foo/bar" in the gui, the + * paths "foo" and "foo/bar" will be sent to the program. + */ + NoTrailingSlashes, + /** + * QStringList + * This should be a qstringlist with either two elements. The first + * string is what PreservePathSwitch in the ExtractArgs will be replaced + * with if PreservePath is True/enabled. The second is for the disabled + * case. An empty string means that the argument will not be used in + * that case. + * Example: for rar, "x" means extract with full paths, and "e" means + * extract without full paths. in this case we will use the stringlist + * ("x", "e"). Or, for another format that might use the switch + * "--extractFull" for preservePaths, and nothing otherwise: we use the + * stringlist ("--extractFull", "") + */ + PreservePathSwitch, + /** + * QStringList (default empty) + * The format of the root node switch. The variable $Path will be + * substituted for the path string. + * Example: ("--internalPath=$Path) + * or ("--path", "$Path") + */ + RootNodeSwitch, + /** + * QStringList (default empty) + * The format of the root node switch. The variable $Password will be + * substituted for the password string. NOTE: supplying passwords + * through a virtual terminal is not supported (yet?), because this + * is not cross platform compatible. As of KDE 4.3 there are no plans to + * change this. + * Example: ("-p$Password) + * or ("--password", "$Password") + */ + PasswordSwitch, + /** + * QString + * This is a regexp, defining how to recognize a "File already exists" + * prompt when extracting. It should have one captured string, which is + * the filename of the file/folder that already exists. + */ + FileExistsExpression, + /** + * int + * This sets on what output channel the FileExistsExpression regex + * should be applied on, in other words, on what stream the "file + * exists" output will appear in. Values accepted: + * 0 - Standard error, stderr (default) + * 1 - Standard output, stdout + */ + FileExistsMode, + /** + * QStringList + * The various responses that can be supplied as a response to the + * "file exists" prompt. The various items are to be supplied in the + * following order: + * index 0 - Yes (overwrite) + * index 1 - No (skip/do not overwrite) + * index 2 - All (overwrite all) + * index 3 - Do not overwrite any files (autoskip) + * index 4 - Cancel operation + */ + FileExistsInput, + + ///////////////[ DELETE ]///////////// + + /** + * QStringList + * The names to the program that will handle deleting of elements in this + * archive format (eg "rar"). Will be searched for in PATH + */ + DeleteProgram, + /** + * QStringList + * The arguments that are passed to the program above for + * deleting from the archive. Special strings that will be + * substituted: + * $Archive - the path of the archive + * $Files - the files selected to be deleted + */ + DeleteArgs, + /** + * QStringList + * Default: empty + * A list of regexp patterns that will cause the extraction to exit + * with a general fail message + */ + ExtractionFailedPatterns, + /** + * QStringList + * Default: empty + * A list of regexp patterns that will alert the user that the password + * was wrong. + */ + WrongPasswordPatterns, + + ///////////////[ ADD ]///////////// + + /** + * QStringList + * The names to the program that will handle adding in this + * archive format (eg "rar"). Will be searched for in PATH + */ + AddProgram, + /** + * QStringList + * The arguments that are passed to the program above for + * adding to the archive. Special strings that will be + * substituted: + * $Archive - the path of the archive + * $Files - the files selected to be added + */ + AddArgs +}; + +typedef QHash ParameterList; + +class KERFUFFLE_EXPORT CliInterface : public ReadWriteArchiveInterface +{ + Q_OBJECT + +public: + enum OperationMode { + List, Copy, Add, Delete + }; + OperationMode m_operationMode; + + explicit CliInterface(QObject *parent, const QVariantList & args); + virtual ~CliInterface(); + + virtual bool list(); + virtual bool copyFiles(const QList & files, const QString & destinationDirectory, ExtractionOptions options); + virtual bool addFiles(const QStringList & files, const CompressionOptions& options); + virtual bool deleteFiles(const QList & files); + + virtual ParameterList parameterList() const = 0; + virtual bool readListLine(const QString &line) = 0; + + bool doKill(); + bool doSuspend(); + bool doResume(); + + /** + * Returns the list of characters which are preceded by a + * backslash when a file name in an archive is passed to + * a program. + * + * @see setEscapedCharacters(). + */ + QString escapedCharacters(); + + /** + * Sets which characters will be preceded by a backslash when + * a file name in an archive is passed to a program. + * + * @see escapedCharacters(). + */ + void setEscapedCharacters(const QString& characters); + + /** + * Sets if the listing should include empty lines. + * + * The default value is false. + */ + void setListEmptyLines(bool emptyLines); + +private: + void substituteListVariables(QStringList& params); + + void cacheParameterList(); + + /** + * Checks whether a line of the program's output is a password prompt. + * + * It uses the regular expression in the @c PasswordPromptPattern parameter + * for the check. + * + * @param line A line of the program's output. + * + * @return @c true if the given @p line is a password prompt, @c false + * otherwise. + */ + + bool checkForPasswordPromptMessage(const QString& line); + + bool checkForFileExistsMessage(const QString& line); + bool handleFileExistsMessage(const QString& filename); + bool checkForErrorMessage(const QString& line, int parameterIndex); + void handleLine(const QString& line); + + void failOperation(); + + /** + * Run @p programName with the given @p arguments. + * The method waits until @p programName is finished to exit. + * + * @param programName The program that will be run (not the whole path). + * @param arguments A list of arguments that will be passed to the program. + * + * @return @c true if the program was found and the process ran correctly, + * @c false otherwise. + */ + bool runProcess(const QStringList& programNames, const QStringList& arguments); + + /** + * Performs any additional escaping and processing on @p fileName + * before passing it to the underlying process. + * + * The default implementation returns @p fileName unchanged. + * + * @param fileName String to escape. + */ + virtual QString escapeFileName(const QString &fileName) const; + + /** + * Wrapper around KProcess::write() or KPtyDevice::write(), depending on + * the platform. + */ + void writeToProcess(const QByteArray& data); + + QByteArray m_stdOutData; + QRegExp m_existsPattern; + QRegExp m_passwordPromptPattern; + QHash > m_patternCache; + +#ifdef Q_OS_WIN + KProcess *m_process; +#else + KPtyProcess *m_process; +#endif + + ParameterList m_param; + QVariantList m_removedFiles; + bool m_listEmptyLines; + bool m_abortingOperation; + +private slots: + void readStdout(bool handleAll = false); + void processFinished(int exitCode, QProcess::ExitStatus exitStatus); +}; +} + +#endif /* CLIINTERFACE_H */ diff --git a/ark/kerfuffle/extractiondialog.cpp b/ark/kerfuffle/extractiondialog.cpp new file mode 100644 index 00000000..f665bc8c --- /dev/null +++ b/ark/kerfuffle/extractiondialog.cpp @@ -0,0 +1,234 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2009 Harald Hvaal + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "extractiondialog.h" +#include "settings.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "ui_extractiondialog.h" + +namespace Kerfuffle +{ + +class ExtractionDialogUI: public QFrame, public Ui::ExtractionDialog +{ +public: + ExtractionDialogUI(QWidget *parent = 0) + : QFrame(parent) { + setupUi(this); + } +}; + +ExtractionDialog::ExtractionDialog(QWidget *parent) + : KDirSelectDialog(KUrl(), false, parent) +{ + m_ui = new ExtractionDialogUI(this); + + mainWidget()->layout()->addWidget(m_ui); + setCaption(i18nc("@title:window", "Extract")); + m_ui->iconLabel->setPixmap(DesktopIcon(QLatin1String( "archive-extract" ))); + + m_ui->filesToExtractGroupBox->hide(); + m_ui->allFilesButton->setChecked(true); + m_ui->extractAllLabel->show(); + + setSingleFolderArchive(false); + + m_ui->autoSubfolders->hide(); + + loadSettings(); + + connect(this, SIGNAL(finished(int)), SLOT(writeSettings())); +} + +void ExtractionDialog::loadSettings() +{ + setOpenDestinationFolderAfterExtraction(ArkSettings::openDestinationFolderAfterExtraction()); + setCloseAfterExtraction(ArkSettings::closeAfterExtraction()); + setPreservePaths(ArkSettings::preservePaths()); +} + +void ExtractionDialog::setSingleFolderArchive(bool value) +{ + m_ui->singleFolderGroup->setChecked(!value); +} + +void ExtractionDialog::batchModeOption() +{ + m_ui->autoSubfolders->show(); + m_ui->autoSubfolders->setEnabled(true); + m_ui->singleFolderGroup->hide(); + m_ui->extractAllLabel->setText(i18n("Extract multiple archives")); +} + +void ExtractionDialog::accept() +{ + if (extractToSubfolder()) { + if (subfolder().contains(QLatin1String( "/" ))) { + KMessageBox::error(NULL, i18n("The subfolder name may not contain the character '/'.")); + return; + } + + const QString pathWithSubfolder = url().pathOrUrl(KUrl::AddTrailingSlash) + subfolder(); + + while (1) { + if (KIO::NetAccess::exists(pathWithSubfolder, KIO::NetAccess::SourceSide, 0)) { + if (QFileInfo(pathWithSubfolder).isDir()) { + int overwrite = KMessageBox::questionYesNoCancel(0, i18nc("@info", "The folder %1 already exists. Are you sure you want to extract here?", pathWithSubfolder), i18n("Folder exists"), KGuiItem(i18n("Extract here")), KGuiItem(i18n("Retry")), KGuiItem(i18n("Cancel"))); + + if (overwrite == KMessageBox::No) { + // The user clicked Retry. + continue; + } else if (overwrite == KMessageBox::Cancel) { + return; + } + } else { + KMessageBox::detailedError(0, + i18nc("@info", "The folder %1 could not be created.", subfolder()), + i18nc("@info", "%1 already exists, but is not a folder.", subfolder())); + return; + } + } else if (!KIO::NetAccess::mkdir(pathWithSubfolder, 0)) { + KMessageBox::detailedError(0, + i18nc("@info", "The folder %1 could not be created.", subfolder()), + i18n("Please check your permissions to create it.")); + return; + } + break; + } + } + + KDirSelectDialog::accept(); +} + +void ExtractionDialog::setSubfolder(const QString& subfolder) +{ + m_ui->subfolder->setText(subfolder); +} + +QString ExtractionDialog::subfolder() const +{ + return m_ui->subfolder->text(); +} + +ExtractionDialog::~ExtractionDialog() +{ + delete m_ui; + m_ui = 0; +} + +void ExtractionDialog::setShowSelectedFiles(bool value) +{ + if (value) { + m_ui->filesToExtractGroupBox->show(); + m_ui->selectedFilesButton->setChecked(true); + m_ui->extractAllLabel->hide(); + } else { + m_ui->filesToExtractGroupBox->hide(); + m_ui->selectedFilesButton->setChecked(false); + m_ui->extractAllLabel->show(); + } +} + +bool ExtractionDialog::extractAllFiles() const +{ + return m_ui->allFilesButton->isChecked(); +} + +void ExtractionDialog::setAutoSubfolder(bool value) +{ + m_ui->autoSubfolders->setChecked(value); +} + +bool ExtractionDialog::autoSubfolders() const +{ + return m_ui->autoSubfolders->isChecked(); +} + +bool ExtractionDialog::extractToSubfolder() const +{ + return m_ui->singleFolderGroup->isChecked(); +} + +void ExtractionDialog::setOpenDestinationFolderAfterExtraction(bool value) +{ + m_ui->openFolderCheckBox->setChecked(value); +} + +void ExtractionDialog::setCloseAfterExtraction(bool value) +{ + m_ui->closeAfterExtraction->setChecked(value); +} + +void ExtractionDialog::setPreservePaths(bool value) +{ + m_ui->preservePaths->setChecked(value); +} + +bool ExtractionDialog::preservePaths() const +{ + return m_ui->preservePaths->isChecked(); +} + +bool ExtractionDialog::openDestinationAfterExtraction() const +{ + return m_ui->openFolderCheckBox->isChecked(); +} + +bool ExtractionDialog::closeAfterExtraction() const +{ + return m_ui->closeAfterExtraction->isChecked(); +} + +KUrl ExtractionDialog::destinationDirectory() const +{ + if (extractToSubfolder()) { + return QString(url().pathOrUrl(KUrl::AddTrailingSlash) + subfolder() + QLatin1Char( '/' )); + } else { + return url().pathOrUrl(KUrl::AddTrailingSlash); + } +} + +void ExtractionDialog::writeSettings() +{ + ArkSettings::setOpenDestinationFolderAfterExtraction(openDestinationAfterExtraction()); + ArkSettings::setCloseAfterExtraction(closeAfterExtraction()); + ArkSettings::setPreservePaths(preservePaths()); + ArkSettings::self()->writeConfig(); +} + +} + +#include "extractiondialog.moc" diff --git a/ark/kerfuffle/extractiondialog.h b/ark/kerfuffle/extractiondialog.h new file mode 100644 index 00000000..03e773b8 --- /dev/null +++ b/ark/kerfuffle/extractiondialog.h @@ -0,0 +1,78 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2007 Henrique Pinto + * Copyright (C) 2008 Harald Hvaal + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EXTRACTIONDIALOG_H +#define EXTRACTIONDIALOG_H + +#include "kerfuffle_export.h" + +#include + +#include + +namespace Kerfuffle +{ +class KERFUFFLE_EXPORT ExtractionDialog : public KDirSelectDialog +{ + Q_OBJECT +public: + ExtractionDialog(QWidget *parent = 0); + virtual ~ExtractionDialog(); + + void setShowSelectedFiles(bool); + void setSingleFolderArchive(bool); + void setPreservePaths(bool); + void batchModeOption(); + void setOpenDestinationFolderAfterExtraction(bool); + void setCloseAfterExtraction(bool); + void setAutoSubfolder(bool value); + + bool extractAllFiles() const; + bool openDestinationAfterExtraction() const; + bool closeAfterExtraction() const; + bool extractToSubfolder() const; + bool autoSubfolders() const; + bool preservePaths() const; + KUrl destinationDirectory() const; + QString subfolder() const; + virtual void accept(); + +public Q_SLOTS: + void setSubfolder(const QString& subfolder); + +private Q_SLOTS: + void writeSettings(); + +private: + void loadSettings(); + + class ExtractionDialogUI *m_ui; +}; +} + +#endif // EXTRACTIONDIALOG_H diff --git a/ark/kerfuffle/extractiondialog.ui b/ark/kerfuffle/extractiondialog.ui new file mode 100644 index 00000000..9a9cc317 --- /dev/null +++ b/ark/kerfuffle/extractiondialog.ui @@ -0,0 +1,220 @@ + + Henrique Pinto <henrique.pinto@kdemail.net> + ExtractionDialog + + + + 0 + 0 + 314 + 422 + + + + + 0 + 0 + + + + Extraction Dialog + + + + + + + + + 0 + 0 + + + + + 256 + 256 + + + + + + + ../../../../../kde-devel/ark-trunk/pics/ox32-action-ark_extract.png + + + false + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 15 + 75 + true + + + + Extract All Files + + + + + + + + + &Extraction into subfolder: + + + true + + + false + + + + + + + 0 + 0 + + + + true + + + + + + + + + + + 0 + 0 + + + + Options + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Open &destination folder after extraction + + + + + + + Close &Ark after extraction + + + + + + + &Preserve paths when extracting + + + + + + + &Automatically create subfolders + + + + + + + + + + + 0 + 0 + + + + Extract + + + false + + + false + + + + + + + 0 + 0 + + + + &Selected files only + + + true + + + + + + + + 0 + 0 + + + + All &files + + + + + + + + + + + KLineEdit + QLineEdit +
klineedit.h
+
+
+ + +
diff --git a/ark/kerfuffle/jobs.cpp b/ark/kerfuffle/jobs.cpp new file mode 100644 index 00000000..472a6e05 --- /dev/null +++ b/ark/kerfuffle/jobs.cpp @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2007 Henrique Pinto + * Copyright (c) 2008-2009 Harald Hvaal + * Copyright (c) 2009-2012 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "jobs.h" + +#include + +#include +#include + +//#define DEBUG_RACECONDITION + +namespace Kerfuffle +{ + +class Job::Private : public QThread +{ +public: + Private(Job *job, QObject *parent = 0) + : QThread(parent) + , q(job) + { + connect(q, SIGNAL(result(KJob*)), SLOT(quit())); + } + + virtual void run(); + +private: + Job *q; +}; + +void Job::Private::run() +{ + q->doWork(); + + if (q->isRunning()) { + exec(); + } + +#ifdef DEBUG_RACECONDITION + QThread::sleep(2); +#endif +} + +Job::Job(ReadOnlyArchiveInterface *interface, QObject *parent) + : KJob(parent) + , m_archiveInterface(interface) + , m_isRunning(false) + , d(new Private(this)) +{ + static bool onlyOnce = false; + if (!onlyOnce) { + qRegisterMetaType >("QPair"); + onlyOnce = true; + } + + setCapabilities(KJob::Killable); +} + +Job::~Job() +{ + if (d->isRunning()) { + d->wait(); + } + + delete d; +} + +ReadOnlyArchiveInterface *Job::archiveInterface() +{ + return m_archiveInterface; +} + +bool Job::isRunning() const +{ + return m_isRunning; +} + +void Job::start() +{ + m_isRunning = true; + d->start(); +} + +void Job::emitResult() +{ + m_isRunning = false; + KJob::emitResult(); +} + +void Job::connectToArchiveInterfaceSignals() +{ + connect(archiveInterface(), SIGNAL(error(QString,QString)), SLOT(onError(QString,QString))); + connect(archiveInterface(), SIGNAL(entry(ArchiveEntry)), SLOT(onEntry(ArchiveEntry))); + connect(archiveInterface(), SIGNAL(entryRemoved(QString)), SLOT(onEntryRemoved(QString))); + connect(archiveInterface(), SIGNAL(progress(double)), SLOT(onProgress(double))); + connect(archiveInterface(), SIGNAL(info(QString)), SLOT(onInfo(QString))); + connect(archiveInterface(), SIGNAL(finished(bool)), SLOT(onFinished(bool)), Qt::DirectConnection); + connect(archiveInterface(), SIGNAL(userQuery(Query*)), SLOT(onUserQuery(Query*))); +} + +void Job::onError(const QString & message, const QString & details) +{ + Q_UNUSED(details) + + setError(1); + setErrorText(message); +} + +void Job::onEntry(const ArchiveEntry & archiveEntry) +{ + emit newEntry(archiveEntry); +} + +void Job::onProgress(double value) +{ + setPercent(static_cast(100.0*value)); +} + +void Job::onInfo(const QString& info) +{ + emit infoMessage(this, info); +} + +void Job::onEntryRemoved(const QString & path) +{ + emit entryRemoved(path); +} + +void Job::onFinished(bool result) +{ + kDebug() << result; + + archiveInterface()->disconnect(this); + + emitResult(); +} + +void Job::onUserQuery(Query *query) +{ + emit userQuery(query); +} + +bool Job::doKill() +{ + kDebug(); + bool ret = archiveInterface()->doKill(); + if (!ret) { + kDebug() << "Killing does not seem to be supported here."; + } + return ret; +} + +ListJob::ListJob(ReadOnlyArchiveInterface *interface, QObject *parent) + : Job(interface, parent) + , m_isSingleFolderArchive(true) + , m_isPasswordProtected(false) + , m_extractedFilesSize(0) +{ + connect(this, SIGNAL(newEntry(ArchiveEntry)), + this, SLOT(onNewEntry(ArchiveEntry))); +} + +void ListJob::doWork() +{ + emit description(this, i18n("Loading archive...")); + connectToArchiveInterfaceSignals(); + bool ret = archiveInterface()->list(); + + if (!archiveInterface()->waitForFinishedSignal()) { + onFinished(ret); + } +} + +qlonglong ListJob::extractedFilesSize() const +{ + return m_extractedFilesSize; +} + +bool ListJob::isPasswordProtected() const +{ + return m_isPasswordProtected; +} + +bool ListJob::isSingleFolderArchive() const +{ + return m_isSingleFolderArchive; +} + +void ListJob::onNewEntry(const ArchiveEntry& entry) +{ + m_extractedFilesSize += entry[ Size ].toLongLong(); + m_isPasswordProtected |= entry [ IsPasswordProtected ].toBool(); + + if (m_isSingleFolderArchive) { + const QString fileName(entry[FileName].toString()); + const QString basePath(fileName.split(QLatin1Char( '/' )).at(0)); + + if (m_basePath.isEmpty()) { + m_basePath = basePath; + m_subfolderName = basePath; + } else { + if (m_basePath != basePath) { + m_isSingleFolderArchive = false; + m_subfolderName.clear(); + } + } + } +} + +QString ListJob::subfolderName() const +{ + return m_subfolderName; +} + +ExtractJob::ExtractJob(const QVariantList& files, const QString& destinationDir, ExtractionOptions options, ReadOnlyArchiveInterface *interface, QObject *parent) + : Job(interface, parent) + , m_files(files) + , m_destinationDir(destinationDir) + , m_options(options) +{ + setDefaultOptions(); +} + +void ExtractJob::doWork() +{ + QString desc; + if (m_files.count() == 0) { + desc = i18n("Extracting all files"); + } else { + desc = i18np("Extracting one file", "Extracting %1 files", m_files.count()); + } + emit description(this, desc); + + connectToArchiveInterfaceSignals(); + + kDebug() << "Starting extraction with selected files:" + << m_files + << "Destination dir:" << m_destinationDir + << "Options:" << m_options; + + bool ret = archiveInterface()->copyFiles(m_files, m_destinationDir, m_options); + + if (!archiveInterface()->waitForFinishedSignal()) { + onFinished(ret); + } +} + +void ExtractJob::setDefaultOptions() +{ + ExtractionOptions defaultOptions; + + defaultOptions[QLatin1String("PreservePaths")] = false; + + ExtractionOptions::const_iterator it = defaultOptions.constBegin(); + for (; it != defaultOptions.constEnd(); ++it) { + if (!m_options.contains(it.key())) { + m_options[it.key()] = it.value(); + } + } +} + +QString ExtractJob::destinationDirectory() const +{ + return m_destinationDir; +} + +ExtractionOptions ExtractJob::extractionOptions() const +{ + return m_options; +} + +AddJob::AddJob(const QStringList& files, const CompressionOptions& options , ReadWriteArchiveInterface *interface, QObject *parent) + : Job(interface, parent) + , m_files(files) + , m_options(options) +{ +} + +void AddJob::doWork() +{ + emit description(this, i18np("Adding a file", "Adding %1 files", m_files.count())); + + ReadWriteArchiveInterface *m_writeInterface = + qobject_cast(archiveInterface()); + + Q_ASSERT(m_writeInterface); + + connectToArchiveInterfaceSignals(); + bool ret = m_writeInterface->addFiles(m_files, m_options); + + if (!archiveInterface()->waitForFinishedSignal()) { + onFinished(ret); + } +} + +DeleteJob::DeleteJob(const QVariantList& files, ReadWriteArchiveInterface *interface, QObject *parent) + : Job(interface, parent) + , m_files(files) +{ +} + +void DeleteJob::doWork() +{ + emit description(this, i18np("Deleting a file from the archive", "Deleting %1 files", m_files.count())); + + ReadWriteArchiveInterface *m_writeInterface = + qobject_cast(archiveInterface()); + + Q_ASSERT(m_writeInterface); + + connectToArchiveInterfaceSignals(); + int ret = m_writeInterface->deleteFiles(m_files); + + if (!archiveInterface()->waitForFinishedSignal()) { + onFinished(ret); + } +} + +} // namespace Kerfuffle + +#include "jobs.moc" diff --git a/ark/kerfuffle/jobs.h b/ark/kerfuffle/jobs.h new file mode 100644 index 00000000..d704dfc7 --- /dev/null +++ b/ark/kerfuffle/jobs.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2007 Henrique Pinto + * Copyright (c) 2008-2009 Harald Hvaal + * Copyright (c) 2009-2012 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JOBS_H +#define JOBS_H + +#include "kerfuffle_export.h" +#include "archiveinterface.h" +#include "archive.h" +#include "queries.h" + +#include +#include +#include +#include + +namespace Kerfuffle +{ + +class ThreadExecution; + +class KERFUFFLE_EXPORT Job : public KJob +{ + Q_OBJECT + +public: + void start(); + + bool isRunning() const; + +protected: + Job(ReadOnlyArchiveInterface *interface, QObject *parent = 0); + virtual ~Job(); + virtual bool doKill(); + virtual void emitResult(); + + ReadOnlyArchiveInterface *archiveInterface(); + + void connectToArchiveInterfaceSignals(); + +public slots: + virtual void doWork() = 0; + +protected slots: + virtual void onError(const QString &message, const QString &details); + virtual void onInfo(const QString &info); + virtual void onEntry(const ArchiveEntry &archiveEntry); + virtual void onProgress(double progress); + virtual void onEntryRemoved(const QString &path); + virtual void onFinished(bool result); + virtual void onUserQuery(Query *query); + +signals: + void entryRemoved(const QString & entry); + void error(const QString& errorMessage, const QString& details); + void newEntry(const ArchiveEntry &); + void userQuery(Kerfuffle::Query*); + +private: + ReadOnlyArchiveInterface *m_archiveInterface; + + bool m_isRunning; + + class Private; + Private * const d; +}; + +class KERFUFFLE_EXPORT ListJob : public Job +{ + Q_OBJECT + +public: + explicit ListJob(ReadOnlyArchiveInterface *interface, QObject *parent = 0); + + qlonglong extractedFilesSize() const; + bool isPasswordProtected() const; + bool isSingleFolderArchive() const; + QString subfolderName() const; + +public slots: + virtual void doWork(); + +private: + bool m_isSingleFolderArchive; + bool m_isPasswordProtected; + QString m_subfolderName; + QString m_basePath; + qlonglong m_extractedFilesSize; + +private slots: + void onNewEntry(const ArchiveEntry&); +}; + +class KERFUFFLE_EXPORT ExtractJob : public Job +{ + Q_OBJECT + +public: + ExtractJob(const QVariantList& files, const QString& destinationDir, ExtractionOptions options, ReadOnlyArchiveInterface *interface, QObject *parent = 0); + + QString destinationDirectory() const; + ExtractionOptions extractionOptions() const; + +public slots: + virtual void doWork(); + +private: + // TODO: Maybe this should be a method if ExtractionOptions were a class? + void setDefaultOptions(); + + QVariantList m_files; + QString m_destinationDir; + ExtractionOptions m_options; +}; + +class KERFUFFLE_EXPORT AddJob : public Job +{ + Q_OBJECT + +public: + AddJob(const QStringList& files, const CompressionOptions& options, ReadWriteArchiveInterface *interface, QObject *parent = 0); + +public slots: + virtual void doWork(); + +private: + QStringList m_files; + CompressionOptions m_options; +}; + +class KERFUFFLE_EXPORT DeleteJob : public Job +{ + Q_OBJECT + +public: + DeleteJob(const QVariantList& files, ReadWriteArchiveInterface *interface, QObject *parent = 0); + +public slots: + virtual void doWork(); + +private: + QVariantList m_files; +}; + +} // namespace Kerfuffle + +#endif // JOBS_H diff --git a/ark/kerfuffle/kerfufflePlugin.desktop b/ark/kerfuffle/kerfufflePlugin.desktop new file mode 100644 index 00000000..3999d24f --- /dev/null +++ b/ark/kerfuffle/kerfufflePlugin.desktop @@ -0,0 +1,74 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=Kerfuffle/Plugin +Comment=Plugin for handling of archive formats for the Kerfuffle library +Comment[ar]=ملحق للتحكم بهيئات الأرشيف لمكتبة Kerfuffle +Comment[ast]=Aplicación pa remanar los formatos de ficheru de la llibrería Kerfuffle +Comment[bg]=Приствка за работа с архиви за Kerfuffle +Comment[bs]=Priključak za rukovanje formatima arhiva za biblioteku Kerfafl +Comment[ca]=Connector per gestionar els formats d'arxiu per la biblioteca Kerfuffle +Comment[ca@valencia]=Connector per gestionar els formats d'arxiu per la biblioteca Kerfuffle +Comment[cs]=Modul pro správu formátů archivů pro knihovnu Kerfuffle +Comment[da]=Plugin til håndtering af arkivformater til Kerfuffle-biblioteket +Comment[de]=Modul zum Umgang mit Archivformaten der Kerfuffle-Bibliothek +Comment[el]=Πρόσθετο χειρισμού μορφών αρχειοθήκης για τη βιβλιοθήκη Kerfuffle +Comment[en_GB]=Plugin for handling of archive formats for the Kerfuffle library +Comment[es]=Complemento para manejar formatos de archivo de la biblioteca Kerfuffle +Comment[et]=Kerfuffle'i teegi arhiivifailide haldamise plugin +Comment[eu]=Kerfuffle liburutegiaren artxibo-formatuak erabiltzeko plugina +Comment[fi]=Kerfuffle-kirjaston pakettimuotojen tuki +Comment[fr]=Module externe de gestion des formats d'archives pour la bibliothèque « Kerfuffle » +Comment[ga]=Breiseán a láimhseálann formáidí cartlainne thar ceann na leabharlainne Kerfuffle +Comment[gl]=Extensión da biblioteca Kerfuffle para manexar formatos de arquivo +Comment[he]=תוסף לניהול פורמטי ארכיונים לספריית Kerfuffle +Comment[hne]=करफुफल लाइब्रेरी बर अभिलेख फारमेट हेंडल करे बर प्लगइन +Comment[hr]=Priključak za upravljanje arhivnim formatima za biblioteku Kerfuffle +Comment[hu]=Archívumkezelő modul a Kerfuffle programkönyvtárhoz +Comment[ia]=Plugin per manear formatos de archivo per le libreria Kerfuffle +Comment[id]=Pengaya untuk menangani format arsip pustaka Kerfuffle +Comment[it]=Estensione per gestire formati di archivio per la libreria Kerfuffle +Comment[ja]=Kerfuffle ライブラリのアーカイブ形式を扱うプラグイン +Comment[kk]=Kerfuffle жиын файлға арналған архив пішіндерімен айналасу плагині +Comment[km]=កម្មវិធីជំនួយ​សម្រាប់​គ្រប់គ្រង​ទ្រង់ទ្រាយ​ប័ណ្ណសារ​សម្រាប់​បណ្ណាល័យ Kerfuffle +Comment[ko]=Kerfuffle 라이브러리를 위한 압축 파일 플러그인 +Comment[lt]=Kerfuffle bibliotekos archyvų formatų palaikymo priedas +Comment[lv]=Arhīva failu tipu izmantošanas spraudnis Kerfuffle bibliotēkai. +Comment[mr]=कर्फफल लायब्ररी करिताचे संग्रह प्रकार हाताळणारे प्लगइन +Comment[nb]=Programtillegg som håndterer arkivformater, for Kerfuffle-biblioteket +Comment[nds]=Archievformaten-Moduul för de Kerfuffle-Bibliotheek +Comment[nl]=Plug-in voor het afhandelen van archiefformaten voor de Kerfuffle-bibliotheek +Comment[nn]=Programtillegg som handsamar arkivformat for Kerfuffle-biblioteket +Comment[pl]=Wtyczka do obsługi formatów archiwów w bibliotece Kerfuffle +Comment[pt]='Plugin' para lidar com os formatos de pacotes da biblioteca Kerfuffle +Comment[pt_BR]=Plugin para manipulação de formatos de arquivo, para a biblioteca Kerfuffle +Comment[ro]=Modul pentru pentru mînuirea formatelor de arhive pentru biblioteca Kerfuffle +Comment[ru]=Расширение для поддержки архивов с помощью библиотеки Kerfuffle +Comment[sk]=Modul pre spracovanie formátov archívov pre knižnicu Kerfuffle +Comment[sl]=Vstavek za ravnanje z arhivi za knjižnico Kerfuffle +Comment[sq]=Plugin që merret me formatet e arkivave për librarinë Kerfuffle +Comment[sr]=Прикључак за руковање форматима архива за библиотеку Керфафл +Comment[sr@ijekavian]=Прикључак за руковање форматима архива за библиотеку Керфафл +Comment[sr@ijekavianlatin]=Priključak za rukovanje formatima arhiva za biblioteku Kerfuffle +Comment[sr@latin]=Priključak za rukovanje formatima arhiva za biblioteku Kerfuffle +Comment[sv]=Insticksprogram för hantering av arkivformat för Kerfuffle-biblioteket +Comment[ta]=கெர்ஃபஃபில் ஃ நூலகத்துக்கு காப்பக வடிவமைப்பை கையாள சொருகிகள் +Comment[th]=ส่วนเสริมของไลบรารี Kerfuffle สำหรับจัดการแฟ้มจัดเก็บแบบต่าง ๆ +Comment[tr]=Kerfuffle kitaplığı için arşiv biçimleri eklentileri +Comment[uk]=Додаток для роботи з форматами архівів для бібліотеки Kerfuffle +Comment[x-test]=xxPlugin for handling of archive formats for the Kerfuffle libraryxx +Comment[zh_CN]=Kerfuffle 库的压缩归档格式处理插件 +Comment[zh_TW]=處理 Kerfuffle 函式庫壓縮格式的外掛程式 + +# Priority. The greater the better. +[PropertyDef::X-KDE-Priority] +Type=int + +# Revision of the plugin API. +[PropertyDef::X-KDE-Kerfuffle-APIRevision] +Type=int + +# Is the plugin read-write or not? (i.e., can it create archives?) +[PropertyDef::X-KDE-Kerfuffle-ReadWrite] +Type=bool + + diff --git a/ark/kerfuffle/kerfuffle_export.h b/ark/kerfuffle/kerfuffle_export.h new file mode 100644 index 00000000..f25c757a --- /dev/null +++ b/ark/kerfuffle/kerfuffle_export.h @@ -0,0 +1,53 @@ +/* + * This file is part of the KDE project + * + * Copyright (C) 2007 David Faure + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef KERFUFFLE_EXPORT_H +#define KERFUFFLE_EXPORT_H + +/* needed for KDE_EXPORT and KDE_IMPORT macros */ +#include +#include + +#ifndef KERFUFFLE_EXPORT +# if defined(MAKE_KERFUFFLE_LIB) +/* We are building this library */ +# define KERFUFFLE_EXPORT KDE_EXPORT +# else +/* We are using this library */ +# define KERFUFFLE_EXPORT KDE_IMPORT +# endif +#endif + +# ifndef KERFUFFLE_EXPORT_DEPRECATED +# define KERFUFFLE_EXPORT_DEPRECATED KDE_DEPRECATED KERFUFFLE_EXPORT +# endif + +#define KERFUFFLE_EXPORT_PLUGIN(p) \ + K_PLUGIN_FACTORY( ArkPluginFactory, registerPlugin< p >(); ) \ + K_EXPORT_PLUGIN( ArkPluginFactory("p") ) + +#endif diff --git a/ark/kerfuffle/queries.cpp b/ark/kerfuffle/queries.cpp new file mode 100644 index 00000000..2067d0f0 --- /dev/null +++ b/ark/kerfuffle/queries.cpp @@ -0,0 +1,204 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2008-2009 Harald Hvaal + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "queries.h" + +#include +#include +#include +#include + +#include +#include + +namespace Kerfuffle +{ +Query::Query() +{ + m_responseMutex.lock(); +} + +QVariant Query::response() +{ + return m_data.value(QLatin1String( "response" )); +} + +void Query::waitForResponse() +{ + kDebug(); + + //if there is no response set yet, wait + if (!m_data.contains(QLatin1String("response"))) { + m_responseCondition.wait(&m_responseMutex); + } + m_responseMutex.unlock(); +} + +void Query::setResponse(QVariant response) +{ + kDebug(); + + m_data[QLatin1String( "response" )] = response; + m_responseCondition.wakeAll(); +} + +OverwriteQuery::OverwriteQuery(const QString &filename) : + m_noRenameMode(false), + m_multiMode(true) +{ + m_data[QLatin1String( "filename" )] = filename; +} + +void OverwriteQuery::execute() +{ + // If we are being called from the KPart, the cursor is probably Qt::WaitCursor + // at the moment (#231974) + QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor)); + + KIO::RenameDialog_Mode mode = (KIO::RenameDialog_Mode)(KIO::M_OVERWRITE | KIO::M_SKIP); + if (m_noRenameMode) { + mode = (KIO::RenameDialog_Mode)(mode | KIO::M_NORENAME); + } + if (m_multiMode) { + mode = (KIO::RenameDialog_Mode)(mode | KIO::M_MULTI); + } + + KUrl sourceUrl(m_data.value(QLatin1String( "filename" )).toString()); + KUrl destUrl(m_data.value(QLatin1String( "filename" )).toString()); + sourceUrl.cleanPath(); + destUrl.cleanPath(); + + QWeakPointer dialog = new KIO::RenameDialog( + NULL, + i18n("File already exists"), + sourceUrl, + destUrl, + mode); + dialog.data()->exec(); + + m_data[QLatin1String("newFilename")] = dialog.data()->newDestUrl().pathOrUrl(); + + setResponse(dialog.data()->result()); + + delete dialog.data(); + + QApplication::restoreOverrideCursor(); +} + +bool OverwriteQuery::responseCancelled() +{ + return m_data.value(QLatin1String( "response" )).toInt() == KIO::R_CANCEL; +} +bool OverwriteQuery::responseOverwriteAll() +{ + return m_data.value(QLatin1String( "response" )).toInt() == KIO::R_OVERWRITE_ALL; +} +bool OverwriteQuery::responseOverwrite() +{ + return m_data.value(QLatin1String( "response" )).toInt() == KIO::R_OVERWRITE; +} + +bool OverwriteQuery::responseRename() +{ + return m_data.value(QLatin1String( "response" )).toInt() == KIO::R_RENAME; +} + +bool OverwriteQuery::responseSkip() +{ + return m_data.value(QLatin1String( "response" )).toInt() == KIO::R_SKIP; +} + +bool OverwriteQuery::responseAutoSkip() +{ + return m_data.value(QLatin1String( "response" )).toInt() == KIO::R_AUTO_SKIP; +} + +QString OverwriteQuery::newFilename() +{ + return m_data.value(QLatin1String( "newFilename" )).toString(); +} + +void OverwriteQuery::setNoRenameMode(bool enableNoRenameMode) +{ + m_noRenameMode = enableNoRenameMode; +} + +bool OverwriteQuery::noRenameMode() +{ + return m_noRenameMode; +} + +void OverwriteQuery::setMultiMode(bool enableMultiMode) +{ + m_multiMode = enableMultiMode; +} + +bool OverwriteQuery::multiMode() +{ + return m_multiMode; +} + +PasswordNeededQuery::PasswordNeededQuery(const QString& archiveFilename, bool incorrectTryAgain) +{ + m_data[QLatin1String( "archiveFilename" )] = archiveFilename; + m_data[QLatin1String( "incorrectTryAgain" )] = incorrectTryAgain; +} + +void PasswordNeededQuery::execute() +{ + // If we are being called from the KPart, the cursor is probably Qt::WaitCursor + // at the moment (#231974) + QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor)); + + QWeakPointer dlg = new KPasswordDialog; + dlg.data()->setPrompt(i18nc("@info", "The archive %1 is password protected. Please enter the password to extract the file.", m_data.value(QLatin1String( "archiveFilename" )).toString())); + + if (m_data.value(QLatin1String("incorrectTryAgain")).toBool()) { + dlg.data()->showErrorMessage(i18n("Incorrect password, please try again."), KPasswordDialog::PasswordError); + } + + const bool notCancelled = dlg.data()->exec(); + const QString password = dlg.data()->password(); + + m_data[QLatin1String("password")] = password; + setResponse(notCancelled && !password.isEmpty()); + + QApplication::restoreOverrideCursor(); + + delete dlg.data(); +} + +QString PasswordNeededQuery::password() +{ + return m_data.value(QLatin1String( "password" )).toString(); +} + +bool PasswordNeededQuery::responseCancelled() +{ + return !m_data.value(QLatin1String( "response" )).toBool(); +} +} diff --git a/ark/kerfuffle/queries.h b/ark/kerfuffle/queries.h new file mode 100644 index 00000000..0abf6890 --- /dev/null +++ b/ark/kerfuffle/queries.h @@ -0,0 +1,111 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2008 Harald Hvaal + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef QUERIES_H +#define QUERIES_H + +#include "kerfuffle_export.h" + +#include +#include +#include +#include +#include + +namespace Kerfuffle +{ + +typedef QHash QueryData; + +class KERFUFFLE_EXPORT Query +{ +public: + /** + * Execute the response. Will happen in the GUI thread, so it's + * safe to use widgets/gui elements here. Must call setResponse + * when done. + */ + virtual void execute() = 0; + + /** + * Will block until the response have been set + */ + void waitForResponse(); + + QVariant response(); + +protected: + /** + * Protected constructor + */ + Query(); + virtual ~Query() {} + + void setResponse(QVariant response); + + QueryData m_data; + +private: + QWaitCondition m_responseCondition; + QMutex m_responseMutex; +}; + +class KERFUFFLE_EXPORT OverwriteQuery : public Query +{ +public: + explicit OverwriteQuery(const QString& filename); + void execute(); + bool responseCancelled(); + bool responseOverwriteAll(); + bool responseOverwrite(); + bool responseRename(); + bool responseSkip(); + bool responseAutoSkip(); + QString newFilename(); + + void setNoRenameMode(bool enableNoRenameMode); + bool noRenameMode(); + void setMultiMode(bool enableMultiMode); + bool multiMode(); +private: + bool m_noRenameMode; + bool m_multiMode; +}; + +class KERFUFFLE_EXPORT PasswordNeededQuery : public Query +{ +public: + explicit PasswordNeededQuery(const QString& archiveFilename, bool incorrectTryAgain = false); + void execute(); + + bool responseCancelled(); + QString password(); +}; + +} + +#endif /* ifndef QUERIES_H */ diff --git a/ark/kerfuffle/settings.kcfgc b/ark/kerfuffle/settings.kcfgc new file mode 100644 index 00000000..de7ea883 --- /dev/null +++ b/ark/kerfuffle/settings.kcfgc @@ -0,0 +1,7 @@ +File=ark.kcfg +ClassName=ArkSettings +Singleton=true +Mutators=true +SetUserTexts=true +IncludeFiles=\"kerfuffle/kerfuffle_export.h\" +Visibility=KERFUFFLE_EXPORT diff --git a/ark/kerfuffle/tests/CMakeLists.txt b/ark/kerfuffle/tests/CMakeLists.txt new file mode 100644 index 00000000..c0285295 --- /dev/null +++ b/ark/kerfuffle/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include_directories(${KERFUFFLE_QJSON_INCLUDE_DIR}) + +set(RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +set(JSONINTERFACE_SOURCES + jsonarchiveinterface.cpp + jsonparser.cpp +) +kde4_add_library(jsoninterface STATIC ${JSONINTERFACE_SOURCES}) + +macro(KERFUFFLE_UNIT_TESTS) + foreach(_testname ${ARGN}) + kde4_add_unit_test(${_testname} NOGUI ${_testname}.cpp) + target_link_libraries(${_testname} jsoninterface kerfuffle ${KDE4_KDEUI_LIBS} ${QT_QTTEST_LIBRARY} ${KERFUFFLE_QJSON_LIBRARIES}) + endforeach(_testname) +endmacro(KERFUFFLE_UNIT_TESTS) + +KERFUFFLE_UNIT_TESTS( + archivetest + jobstest +) diff --git a/ark/kerfuffle/tests/archivetest.cpp b/ark/kerfuffle/tests/archivetest.cpp new file mode 100644 index 00000000..ff268828 --- /dev/null +++ b/ark/kerfuffle/tests/archivetest.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2010-2011 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "kerfuffle/archive.h" + +#include + +class ArchiveTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void testFileName(); + void testIsPasswordProtected(); + void testOpenNonExistentFile(); +}; + +QTEST_KDEMAIN_CORE(ArchiveTest) + +void ArchiveTest::testFileName() +{ + Kerfuffle::Archive *archive = Kerfuffle::Archive::create(QLatin1String("/tmp/foo.tar.gz"), this); + + if (!archive) { + QSKIP("There is no plugin to handle tar.gz files. Skipping test.", SkipSingle); + } + + QCOMPARE(archive->fileName(), QLatin1String("/tmp/foo.tar.gz")); +} + +void ArchiveTest::testIsPasswordProtected() +{ + Kerfuffle::Archive *archive; + + archive = Kerfuffle::Archive::create(QLatin1String(KDESRCDIR "data/archivetest_encrypted.zip"), this); + if (!archive) { + QSKIP("There is no plugin to handle zip files. Skipping test.", SkipSingle); + } + + QVERIFY(archive->isPasswordProtected()); + + archive->deleteLater(); + + archive = Kerfuffle::Archive::create(QLatin1String(KDESRCDIR "data/archivetest_unencrypted.zip"), this); + + QVERIFY(!archive->isPasswordProtected()); +} + +void ArchiveTest::testOpenNonExistentFile() +{ + QSKIP("How should we deal with files that do not exist? Should factory() return NULL?", SkipSingle); +} + +#include "archivetest.moc" diff --git a/ark/kerfuffle/tests/data/archive-deepsinglehierarchy.json b/ark/kerfuffle/tests/data/archive-deepsinglehierarchy.json new file mode 100644 index 00000000..d673c7c0 --- /dev/null +++ b/ark/kerfuffle/tests/data/archive-deepsinglehierarchy.json @@ -0,0 +1,20 @@ +[ + { + "FileName": "aDir/", + "IsDirectory": true + }, + { + "FileName": "aDir/b.txt" + }, + { + "FileName": "aDir/aDirInside/", + "IsDirectory": true + }, + { + "FileName": "aDir/aDirInside/anotherDir/", + "IsDirectory": true + }, + { + "FileName": "aDir/aDirInside/anotherDir/file.txt" + } +] diff --git a/ark/kerfuffle/tests/data/archive-multiplefolders.json b/ark/kerfuffle/tests/data/archive-multiplefolders.json new file mode 100644 index 00000000..92e9224c --- /dev/null +++ b/ark/kerfuffle/tests/data/archive-multiplefolders.json @@ -0,0 +1,16 @@ +[ + { + "FileName": "aDir/", + "IsDirectory": true + }, + { + "FileName": "aDir/b.txt" + }, + { + "FileName": "anotherDir/", + "IsDirectory": true + }, + { + "FileName": "anotherDir/file.txt" + } +] diff --git a/ark/kerfuffle/tests/data/archive-nodir-manyfiles.json b/ark/kerfuffle/tests/data/archive-nodir-manyfiles.json new file mode 100644 index 00000000..945c6168 --- /dev/null +++ b/ark/kerfuffle/tests/data/archive-nodir-manyfiles.json @@ -0,0 +1,8 @@ +[ + { + "FileName": "a.txt" + }, + { + "FileName": "file.txt" + } +] diff --git a/ark/kerfuffle/tests/data/archive-onetopfolder.json b/ark/kerfuffle/tests/data/archive-onetopfolder.json new file mode 100644 index 00000000..ec1e2ef5 --- /dev/null +++ b/ark/kerfuffle/tests/data/archive-onetopfolder.json @@ -0,0 +1,9 @@ +[ + { + "FileName": "aDir/", + "IsDirectory": true + }, + { + "FileName": "aDir/b.txt" + } +] diff --git a/ark/kerfuffle/tests/data/archive-password.json b/ark/kerfuffle/tests/data/archive-password.json new file mode 100644 index 00000000..a6abd9bf --- /dev/null +++ b/ark/kerfuffle/tests/data/archive-password.json @@ -0,0 +1,13 @@ +[ + { + "FileName": "foo.txt", + "IsPasswordProtected": true + }, + { + "FileName": "bar.txt" + }, + { + "FileName": "aDirectory/", + "IsDirectory": true + } +] diff --git a/ark/kerfuffle/tests/data/archive-singlefile.json b/ark/kerfuffle/tests/data/archive-singlefile.json new file mode 100644 index 00000000..21d49f77 --- /dev/null +++ b/ark/kerfuffle/tests/data/archive-singlefile.json @@ -0,0 +1,5 @@ +[ + { + "FileName": "a.txt" + } +] \ No newline at end of file diff --git a/ark/kerfuffle/tests/data/archive-unorderedsinglefolder.json b/ark/kerfuffle/tests/data/archive-unorderedsinglefolder.json new file mode 100644 index 00000000..28db1cff --- /dev/null +++ b/ark/kerfuffle/tests/data/archive-unorderedsinglefolder.json @@ -0,0 +1,16 @@ +[ + { + "FileName": "aDir/anotherDir/bar.txt" + }, + { + "FileName": "aDir/foo.txt" + }, + { + "FileName": "aDir/anotherDir/", + "IsDirectory": true + }, + { + "FileName": "aDir/", + "IsDirectory": true + } +] diff --git a/ark/kerfuffle/tests/data/archive001.json b/ark/kerfuffle/tests/data/archive001.json new file mode 100644 index 00000000..01a78601 --- /dev/null +++ b/ark/kerfuffle/tests/data/archive001.json @@ -0,0 +1,15 @@ +[ + { + "FileName": "a.txt" + }, + { + "FileName": "aDir/", + "IsDirectory": true + }, + { + "FileName": "aDir/b.txt" + }, + { + "FileName": "c.txt" + } +] diff --git a/ark/kerfuffle/tests/data/archive002.json b/ark/kerfuffle/tests/data/archive002.json new file mode 100644 index 00000000..5aa50b69 --- /dev/null +++ b/ark/kerfuffle/tests/data/archive002.json @@ -0,0 +1,18 @@ +[ + { + "FileName": "a.txt", + "Size": 5 + }, + { + "FileName": "aDir/", + "IsDirectory": true + }, + { + "FileName": "aDir/b.txt", + "Size": 954 + }, + { + "FileName": "c.txt", + "Size": 45000 + } +] diff --git a/ark/kerfuffle/tests/data/archivetest_encrypted.zip b/ark/kerfuffle/tests/data/archivetest_encrypted.zip new file mode 100644 index 00000000..3a1093df Binary files /dev/null and b/ark/kerfuffle/tests/data/archivetest_encrypted.zip differ diff --git a/ark/kerfuffle/tests/data/archivetest_unencrypted.zip b/ark/kerfuffle/tests/data/archivetest_unencrypted.zip new file mode 100644 index 00000000..5bc97e67 Binary files /dev/null and b/ark/kerfuffle/tests/data/archivetest_unencrypted.zip differ diff --git a/ark/kerfuffle/tests/data/simplearchive.tar.gz b/ark/kerfuffle/tests/data/simplearchive.tar.gz new file mode 100644 index 00000000..382144c7 Binary files /dev/null and b/ark/kerfuffle/tests/data/simplearchive.tar.gz differ diff --git a/ark/kerfuffle/tests/jobstest.cpp b/ark/kerfuffle/tests/jobstest.cpp new file mode 100644 index 00000000..45fd77d8 --- /dev/null +++ b/ark/kerfuffle/tests/jobstest.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2010-2011 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "kerfuffle/jobs.h" + +#include "jsonarchiveinterface.h" + +#include +#include +#include + +#include +#include + +using Kerfuffle::ArchiveEntry; + +class JobsTest : public QObject +{ + Q_OBJECT + +public: + JobsTest(); + +protected Q_SLOTS: + void init(); + + void slotNewEntry(const ArchiveEntry& entry); + +private Q_SLOTS: + // ListJob-related tests + void testExtractedFilesSize(); + void testIsPasswordProtected(); + void testIsSingleFolderArchive(); + void testListEntries(); + + // ExtractJob-related tests + void testExtractJobAccessors(); + + // DeleteJob-related tests + void testRemoveEntry(); + + // AddJob-related tests + void testAddEntry(); + +private: + JSONArchiveInterface *createArchiveInterface(const QString& filePath); + QList listEntries(JSONArchiveInterface *iface); + void startAndWaitForResult(KJob *job); + + QList m_entries; + QEventLoop m_eventLoop; +}; + +QTEST_KDEMAIN_CORE(JobsTest) + +JobsTest::JobsTest() + : QObject(0) + , m_eventLoop(this) +{ + // Hackish way to make sure the i18n stuff + // is called from the main thread + KGlobal::locale(); + + qRegisterMetaType("ArchiveEntry"); +} + +void JobsTest::init() +{ + m_entries.clear(); +} + +JSONArchiveInterface *JobsTest::createArchiveInterface(const QString& filePath) +{ + QVariantList args; + args.append(filePath); + + JSONArchiveInterface *iface = new JSONArchiveInterface(this, args); + if (!iface->open()) { + kDebug() << "Could not open" << filePath; + return NULL; + } + + return iface; +} + +void JobsTest::startAndWaitForResult(KJob *job) +{ + connect(job, SIGNAL(result(KJob*)), &m_eventLoop, SLOT(quit())); + job->start(); + m_eventLoop.exec(); +} + +void JobsTest::testExtractedFilesSize() +{ + Kerfuffle::ListJob *listJob; + + JSONArchiveInterface *noSizeIface = + createArchiveInterface(QLatin1String(KDESRCDIR "data/archive001.json")); + JSONArchiveInterface *sizeIface = + createArchiveInterface(QLatin1String(KDESRCDIR "data/archive002.json")); + + listJob = new Kerfuffle::ListJob(noSizeIface, this); + listJob->setAutoDelete(false); + startAndWaitForResult(listJob); + + QCOMPARE(listJob->extractedFilesSize(), 0LL); + + listJob = new Kerfuffle::ListJob(sizeIface, this); + listJob->setAutoDelete(false); + startAndWaitForResult(listJob); + + QCOMPARE(listJob->extractedFilesSize(), 45959LL); + + noSizeIface->deleteLater(); + sizeIface->deleteLater(); +} + +void JobsTest::testIsPasswordProtected() +{ + Kerfuffle::ListJob *listJob; + + JSONArchiveInterface *noPasswordIface = + createArchiveInterface(QLatin1String(KDESRCDIR "data/archive002.json")); + JSONArchiveInterface *passwordIface = + createArchiveInterface(QLatin1String(KDESRCDIR "data/archive-password.json")); + + listJob = new Kerfuffle::ListJob(noPasswordIface, this); + listJob->setAutoDelete(false); + startAndWaitForResult(listJob); + + QVERIFY(!listJob->isPasswordProtected()); + + listJob = new Kerfuffle::ListJob(passwordIface, this); + listJob->setAutoDelete(false); + startAndWaitForResult(listJob); + + QVERIFY(listJob->isPasswordProtected()); + + noPasswordIface->deleteLater(); + passwordIface->deleteLater(); +} + +void JobsTest::testIsSingleFolderArchive() +{ + JSONArchiveInterface *iface = + createArchiveInterface(QLatin1String(KDESRCDIR "data/archive001.json")); + + Kerfuffle::ListJob *listJob = new Kerfuffle::ListJob(iface, this); + listJob->setAutoDelete(false); + startAndWaitForResult(listJob); + QVERIFY(!listJob->isSingleFolderArchive()); + QCOMPARE(listJob->subfolderName(), QString()); + iface->deleteLater(); + + iface = createArchiveInterface(QLatin1String(KDESRCDIR "data/archive-singlefile.json")); + listJob = new Kerfuffle::ListJob(iface, this); + listJob->setAutoDelete(false); + startAndWaitForResult(listJob); + QVERIFY(listJob->isSingleFolderArchive()); + QCOMPARE(listJob->subfolderName(), QLatin1String("a.txt")); + iface->deleteLater(); + + iface = createArchiveInterface(QLatin1String(KDESRCDIR "data/archive-onetopfolder.json")); + listJob = new Kerfuffle::ListJob(iface, this); + listJob->setAutoDelete(false); + startAndWaitForResult(listJob); + QVERIFY(listJob->isSingleFolderArchive()); + QCOMPARE(listJob->subfolderName(), QLatin1String("aDir")); + iface->deleteLater(); + + iface = createArchiveInterface + (QLatin1String(KDESRCDIR "data/archive-multiplefolders.json")); + listJob = new Kerfuffle::ListJob(iface, this); + listJob->setAutoDelete(false); + startAndWaitForResult(listJob); + QVERIFY(!listJob->isSingleFolderArchive()); + QCOMPARE(listJob->subfolderName(), QString()); + iface->deleteLater(); + + iface = createArchiveInterface + (QLatin1String(KDESRCDIR "data/archive-nodir-manyfiles.json")); + listJob = new Kerfuffle::ListJob(iface, this); + listJob->setAutoDelete(false); + startAndWaitForResult(listJob); + QVERIFY(!listJob->isSingleFolderArchive()); + QCOMPARE(listJob->subfolderName(), QString()); + iface->deleteLater(); + + iface = createArchiveInterface + (QLatin1String(KDESRCDIR "data/archive-deepsinglehierarchy.json")); + listJob = new Kerfuffle::ListJob(iface, this); + listJob->setAutoDelete(false); + startAndWaitForResult(listJob); + QVERIFY(listJob->isSingleFolderArchive()); + QCOMPARE(listJob->subfolderName(), QLatin1String("aDir")); + iface->deleteLater(); + + iface = createArchiveInterface + (QLatin1String(KDESRCDIR "data/archive-unorderedsinglefolder.json")); + listJob = new Kerfuffle::ListJob(iface, this); + listJob->setAutoDelete(false); + startAndWaitForResult(listJob); + QVERIFY(listJob->isSingleFolderArchive()); + QCOMPARE(listJob->subfolderName(), QLatin1String("aDir")); + iface->deleteLater(); +} + +void JobsTest::testListEntries() +{ + JSONArchiveInterface *iface = + createArchiveInterface(QLatin1String(KDESRCDIR "data/archive001.json")); + + QList archiveEntries(listEntries(iface)); + + QStringList entries; + entries.append(QLatin1String("a.txt")); + entries.append(QLatin1String("aDir/")); + entries.append(QLatin1String("aDir/b.txt")); + entries.append(QLatin1String("c.txt")); + + QCOMPARE(entries.count(), archiveEntries.count()); + + for (int i = 0; i < entries.count(); ++i) { + Kerfuffle::ArchiveEntry e(archiveEntries.at(i)); + + QCOMPARE(entries[i], e[Kerfuffle::FileName].toString()); + } + + iface->deleteLater(); +} + +void JobsTest::slotNewEntry(const ArchiveEntry& entry) +{ + m_entries.append(entry); +} + +QList JobsTest::listEntries(JSONArchiveInterface *iface) +{ + m_entries.clear(); + + Kerfuffle::ListJob *listJob = new Kerfuffle::ListJob(iface, this); + connect(listJob, SIGNAL(newEntry(ArchiveEntry)), + SLOT(slotNewEntry(ArchiveEntry))); + + startAndWaitForResult(listJob); + + return m_entries; +} + +void JobsTest::testExtractJobAccessors() +{ + JSONArchiveInterface *iface = createArchiveInterface(QLatin1String(KDESRCDIR "data/archive001.json")); + Kerfuffle::ExtractJob *job = + new Kerfuffle::ExtractJob(QVariantList(), QLatin1String("/tmp/some-dir"), + Kerfuffle::ExtractionOptions(), iface, this); + Kerfuffle::ExtractionOptions defaultOptions; + defaultOptions[QLatin1String("PreservePaths")] = false; + + QCOMPARE(job->destinationDirectory(), QLatin1String("/tmp/some-dir")); + QCOMPARE(job->extractionOptions(), defaultOptions); + + job->setAutoDelete(false); + startAndWaitForResult(job); + + QCOMPARE(job->destinationDirectory(), QLatin1String("/tmp/some-dir")); + QCOMPARE(job->extractionOptions(), defaultOptions); + + Kerfuffle::ExtractionOptions options; + options[QLatin1String("PreservePaths")] = true; + options[QLatin1String("foo")] = QLatin1String("bar"); + options[QLatin1String("pi")] = 3.14f; + + job = new Kerfuffle::ExtractJob(QVariantList(), QLatin1String("/root"), + options, iface, this); + + QCOMPARE(job->destinationDirectory(), QLatin1String("/root")); + QCOMPARE(job->extractionOptions(), options); + + job->setAutoDelete(false); + startAndWaitForResult(job); + + QCOMPARE(job->destinationDirectory(), QLatin1String("/root")); + QCOMPARE(job->extractionOptions(), options); +} + +void JobsTest::testRemoveEntry() +{ + QVariantList filesToDelete; + JSONArchiveInterface *iface; + Kerfuffle::DeleteJob *deleteJob; + QStringList expectedEntries; + + filesToDelete.append(QLatin1String("c.txt")); + iface = createArchiveInterface(QLatin1String(KDESRCDIR "data/archive001.json")); + deleteJob = new Kerfuffle::DeleteJob(filesToDelete, iface, this); + startAndWaitForResult(deleteJob); + QList archiveEntries(listEntries(iface)); + expectedEntries.append(QLatin1String("a.txt")); + expectedEntries.append(QLatin1String("aDir/")); + expectedEntries.append(QLatin1String("aDir/b.txt")); + QCOMPARE(archiveEntries.count(), expectedEntries.count()); + for (int i = 0; i < expectedEntries.count(); ++i) { + const Kerfuffle::ArchiveEntry e(archiveEntries.at(i)); + QCOMPARE(expectedEntries[i], e[Kerfuffle::FileName].toString()); + } + iface->deleteLater(); + + // TODO: test for errors +} + +void JobsTest::testAddEntry() +{ + JSONArchiveInterface *iface = createArchiveInterface(QLatin1String(KDESRCDIR "data/archive001.json")); + + QList archiveEntries = listEntries(iface); + QCOMPARE(archiveEntries.count(), 4); + + QStringList newEntries = QStringList() << QLatin1String("foo"); + + Kerfuffle::AddJob *addJob = + new Kerfuffle::AddJob(newEntries, Kerfuffle::CompressionOptions(), iface, this); + startAndWaitForResult(addJob); + + archiveEntries = listEntries(iface); + QCOMPARE(archiveEntries.count(), 5); + + addJob = new Kerfuffle::AddJob(newEntries, Kerfuffle::CompressionOptions(), iface, this); + startAndWaitForResult(addJob); + + archiveEntries = listEntries(iface); + QCOMPARE(archiveEntries.count(), 5); + + newEntries = QStringList() << QLatin1String("bar") << QLatin1String("aDir/test.txt"); + + addJob = new Kerfuffle::AddJob(newEntries, Kerfuffle::CompressionOptions(), iface, this); + startAndWaitForResult(addJob); + + archiveEntries = listEntries(iface); + QCOMPARE(archiveEntries.count(), 7); + + iface->deleteLater(); +} + +#include "jobstest.moc" diff --git a/ark/kerfuffle/tests/jsonarchiveinterface.cpp b/ark/kerfuffle/tests/jsonarchiveinterface.cpp new file mode 100644 index 00000000..14e62f44 --- /dev/null +++ b/ark/kerfuffle/tests/jsonarchiveinterface.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2010-2011 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "jsonarchiveinterface.h" + +#include +#include + +#include + +JSONArchiveInterface::JSONArchiveInterface(QObject *parent, const QVariantList& args) + : Kerfuffle::ReadWriteArchiveInterface(parent, args) +{ +} + +JSONArchiveInterface::~JSONArchiveInterface() +{ +} + +bool JSONArchiveInterface::list() +{ + JSONParser::JSONArchive::const_iterator it = m_archive.constBegin(); + for (; it != m_archive.constEnd(); ++it) { + emit entry(*it); + } + + return true; +} + +bool JSONArchiveInterface::open() +{ + QFile file(filename()); + + if (!file.exists()) { + return false; + } + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return false; + } + + m_archive = JSONParser::parse(&file); + + return !m_archive.isEmpty(); +} + +bool JSONArchiveInterface::addFiles(const QStringList& files, const Kerfuffle::CompressionOptions& options) +{ + Q_UNUSED(options) + + foreach (const QString& file, files) { + if (m_archive.contains(file)) { + return false; + } + + Kerfuffle::ArchiveEntry e; + e[Kerfuffle::FileName] = file; + + m_archive[file] = e; + } + + return true; +} + +bool JSONArchiveInterface::copyFiles(const QList& files, const QString& destinationDirectory, Kerfuffle::ExtractionOptions options) +{ + Q_UNUSED(files) + Q_UNUSED(destinationDirectory) + Q_UNUSED(options) + + return true; +} + +bool JSONArchiveInterface::deleteFiles(const QList& files) +{ + foreach (const QVariant& file, files) { + const QString fileName = file.toString(); + if (m_archive.contains(fileName)) { + m_archive.remove(fileName); + emit entryRemoved(fileName); + } + } + + return true; +} + +#include "jsonarchiveinterface.moc" diff --git a/ark/kerfuffle/tests/jsonarchiveinterface.h b/ark/kerfuffle/tests/jsonarchiveinterface.h new file mode 100644 index 00000000..e74c4f1b --- /dev/null +++ b/ark/kerfuffle/tests/jsonarchiveinterface.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2010-2011 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSONARCHIVEINTERFACE_H +#define JSONARCHIVEINTERFACE_H + +#include "jsonparser.h" + +#include "kerfuffle/archive.h" +#include "kerfuffle/archiveinterface.h" + +/** + * A dummy archive interface used by our test cases. + * + * It reads a JSON file which defines the contents of the archive. + * For the file format description, see the documentation for @c JSONParser. + * + * The file's content is read to memory when open() is called and the archive + * is then closed. This means that this class never changes the file's content + * on disk, and entry addition or deletion do not change the original file. + * + * @sa JSONParser + * + * @author Raphael Kubo da Costa + */ +class JSONArchiveInterface : public Kerfuffle::ReadWriteArchiveInterface +{ + Q_OBJECT + +public: + explicit JSONArchiveInterface(QObject *parent, const QVariantList& args); + virtual ~JSONArchiveInterface(); + + virtual bool list(); + virtual bool open(); + + virtual bool addFiles(const QStringList& files, const Kerfuffle::CompressionOptions& options); + virtual bool copyFiles(const QList& files, const QString& destinationDirectory, Kerfuffle::ExtractionOptions options); + virtual bool deleteFiles(const QList& files); + +private: + JSONParser::JSONArchive m_archive; +}; + +#endif diff --git a/ark/kerfuffle/tests/jsonparser.cpp b/ark/kerfuffle/tests/jsonparser.cpp new file mode 100644 index 00000000..2693f348 --- /dev/null +++ b/ark/kerfuffle/tests/jsonparser.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2011 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "jsonparser.h" + +#include "kerfuffle/archiveinterface.h" + +#include + +#include + +#include + +typedef QMap ArchiveProperties; + +static ArchiveProperties archiveProperties() +{ + static ArchiveProperties properties; + + if (!properties.isEmpty()) { + return properties; + } + + properties[QLatin1String("FileName")] = Kerfuffle::FileName; + properties[QLatin1String("InternalID")] = Kerfuffle::InternalID; + properties[QLatin1String("Permissions")] = Kerfuffle::Permissions; + properties[QLatin1String("Owner")] = Kerfuffle::Owner; + properties[QLatin1String("Group")] = Kerfuffle::Group; + properties[QLatin1String("Size")] = Kerfuffle::Size; + properties[QLatin1String("CompressedSize")] = Kerfuffle::CompressedSize; + properties[QLatin1String("Link")] = Kerfuffle::Link; + properties[QLatin1String("Ratio")] = Kerfuffle::Ratio; + properties[QLatin1String("CRC")] = Kerfuffle::CRC; + properties[QLatin1String("Method")] = Kerfuffle::Method; + properties[QLatin1String("Version")] = Kerfuffle::Version; + properties[QLatin1String("Timestamp")] = Kerfuffle::Timestamp; + properties[QLatin1String("IsDirectory")] = Kerfuffle::IsDirectory; + properties[QLatin1String("Comment")] = Kerfuffle::Comment; + properties[QLatin1String("IsPasswordProtected")] = Kerfuffle::IsPasswordProtected; + + return properties; +} + +JSONParser::JSONParser() +{ +} + +JSONParser::~JSONParser() +{ +} + +JSONParser::JSONArchive JSONParser::parse(const QString &json) +{ + bool ok; + QJson::Parser parser; + + const QVariant result = parser.parse(json.toUtf8(), &ok); + + if (!ok) { + kDebug() << "Line" << parser.errorLine() << ":" << parser.errorString(); + return JSONParser::JSONArchive(); + } + + return createJSONArchive(result); +} + +JSONParser::JSONArchive JSONParser::parse(QIODevice *json) +{ + bool ok; + QJson::Parser parser; + + const QVariant result = parser.parse(json, &ok); + + if (!ok) { + kDebug() << "Line" << parser.errorLine() << ":" << parser.errorString(); + return JSONParser::JSONArchive(); + } + + return createJSONArchive(result); +} + +JSONParser::JSONArchive JSONParser::createJSONArchive(const QVariant &json) +{ + static const ArchiveProperties properties = archiveProperties(); + + JSONParser::JSONArchive archive; + + foreach (const QVariant &entry, json.toList()) { + const QVariantMap entryMap = entry.toMap(); + + if (!entryMap.contains(QLatin1String("FileName"))) { + continue; + } + + Kerfuffle::ArchiveEntry archiveEntry; + + QVariantMap::const_iterator entryIterator = entryMap.constBegin(); + for (; entryIterator != entryMap.constEnd(); ++entryIterator) { + if (properties.contains(entryIterator.key())) { + archiveEntry[properties[entryIterator.key()]] = entryIterator.value(); + } else { + kDebug() << entryIterator.key() << "is not a valid entry key"; + } + } + + const QString fileName = entryMap[QLatin1String("FileName")].toString(); + archive[fileName] = archiveEntry; + } + + return archive; +} diff --git a/ark/kerfuffle/tests/jsonparser.h b/ark/kerfuffle/tests/jsonparser.h new file mode 100644 index 00000000..91005dcd --- /dev/null +++ b/ark/kerfuffle/tests/jsonparser.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSONPARSER_H +#define JSONPARSER_H + +#include "kerfuffle/archive.h" + +#include +#include +#include + +/** + * Simple parser which reads JSON files and creates @c ArchiveEntry objects + * from it. + * + * The JSON file is expected to follow a specific format that describes an + * archive read by Kerfuffle. + * + * The format consists of a list of dictionaries whose keys are values from the + * EntryMetaDataType enum. The only required key for each entry is FileName; + * other values which are omitted for each entry are assumed to be 0 or false. + * + * Example file: + * @code + * [ + * { "FileName": "foo", "IsPasswordProtected": true }, + * { "FileName": "aDir/", "IsDirectory": true } + * ] + * @endcode + * + * @author Raphael Kubo da Costa + */ +class JSONParser +{ +public: + typedef QMap JSONArchive; + + ~JSONParser(); + + static JSONArchive parse(const QString &json); + static JSONArchive parse(QIODevice *json); + +private: + JSONParser(); + + /** + * Parses each entry in the QVariant obtained from parsing a JSON file and + * creates a @c JSONArchive from them. + * + * If an entry does not have a "FileName" key, it is ignored. Keys which do + * not correspond to a value in the EntryMetaDataType enum are ignored. + * + * @return A new @c JSONArchive corresponding to the parsed JSON file. If a + * parsing error occurs, it is empty. + */ + static JSONArchive createJSONArchive(const QVariant &json); +}; + +#endif // JSONPARSER_H diff --git a/ark/part/CMakeLists.txt b/ark/part/CMakeLists.txt new file mode 100644 index 00000000..a75d9b26 --- /dev/null +++ b/ark/part/CMakeLists.txt @@ -0,0 +1,30 @@ +set(arkpart_PART_SRCS + part.cpp + infopanel.cpp + arkviewer.cpp + archivemodel.cpp + archiveview.cpp + jobtracker.cpp + ) + +qt4_add_dbus_adaptor(arkpart_PART_SRCS dnddbusinterface.xml part.h Ark::Part) + +kde4_add_ui_files(arkpart_PART_SRCS infopanel.ui ) +kde4_add_ui_files(arkpart_PART_SRCS jobtracker.ui ) + +kde4_add_plugin(arkpart ${arkpart_PART_SRCS}) + +target_link_libraries(arkpart kerfuffle ${KDE4_KFILE_LIBS} ${KDE4_KPARTS_LIBS} ${KDE4_KDEUI_LIBS}) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/ark_part.desktop.cmake + ${CMAKE_CURRENT_BINARY_DIR}/ark_part.desktop +) + +install(TARGETS arkpart DESTINATION ${PLUGIN_INSTALL_DIR}) + + +########### install files ############### + +install( FILES ${CMAKE_CURRENT_BINARY_DIR}/ark_part.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES ark_part.rc DESTINATION ${DATA_INSTALL_DIR}/ark ) diff --git a/ark/part/archivemodel.cpp b/ark/part/archivemodel.cpp new file mode 100644 index 00000000..43262689 --- /dev/null +++ b/ark/part/archivemodel.cpp @@ -0,0 +1,986 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2007 Henrique Pinto + * Copyright (C) 2008-2009 Harald Hvaal + * Copyright (C) 2010-2012 Raphael Kubo da Costa + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "archivemodel.h" +#include "kerfuffle/archive.h" +#include "kerfuffle/jobs.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Kerfuffle; + +class ArchiveDirNode; + +//used to speed up the loading of large archives +static ArchiveNode* s_previousMatch = NULL; +K_GLOBAL_STATIC(QStringList, s_previousPieces) + + +// TODO: This class hierarchy needs some love. +// Having a parent take a child class as a parameter in the constructor +// should trigger one's spider-sense (TM). +class ArchiveNode +{ +public: + ArchiveNode(ArchiveDirNode *parent, const ArchiveEntry & entry) + : m_parent(parent) + { + setEntry(entry); + } + + virtual ~ArchiveNode() + { + } + + const ArchiveEntry &entry() const + { + return m_entry; + } + + void setEntry(const ArchiveEntry& entry) + { + m_entry = entry; + + const QStringList pieces = entry[FileName].toString().split(QLatin1Char( '/' ), QString::SkipEmptyParts); + m_name = pieces.isEmpty() ? QString() : pieces.last(); + + if (entry[IsDirectory].toBool()) { + m_icon = KIconLoader::global()->loadMimeTypeIcon(KMimeType::mimeType(QLatin1String("inode/directory"))->iconName(), KIconLoader::Small); + } else { + const KMimeType::Ptr mimeType = KMimeType::findByPath(m_entry[FileName].toString(), 0, true); + m_icon = KIconLoader::global()->loadMimeTypeIcon(mimeType->iconName(), KIconLoader::Small); + } + } + + ArchiveDirNode *parent() const + { + return m_parent; + } + + int row() const; + + virtual bool isDir() const + { + return false; + } + + QPixmap icon() const + { + return m_icon; + } + + QString name() const + { + return m_name; + } + +protected: + void setIcon(const QPixmap &icon) + { + m_icon = icon; + } + +private: + ArchiveEntry m_entry; + QPixmap m_icon; + QString m_name; + ArchiveDirNode *m_parent; +}; + + +class ArchiveDirNode: public ArchiveNode +{ +public: + ArchiveDirNode(ArchiveDirNode *parent, const ArchiveEntry & entry) + : ArchiveNode(parent, entry) + { + } + + ~ArchiveDirNode() + { + clear(); + } + + QList entries() + { + return m_entries; + } + + void setEntryAt(int index, ArchiveNode* value) + { + m_entries[index] = value; + } + + void appendEntry(ArchiveNode* entry) + { + m_entries.append(entry); + } + + void removeEntryAt(int index) + { + delete m_entries.takeAt(index); + } + + virtual bool isDir() const + { + return true; + } + + ArchiveNode* find(const QString & name) + { + foreach(ArchiveNode *node, m_entries) { + if (node && (node->name() == name)) { + return node; + } + } + return 0; + } + + ArchiveNode* findByPath(const QStringList & pieces, int index = 0) + { + if (index == pieces.count()) { + return 0; + } + + ArchiveNode *next = find(pieces.at(index)); + + if (index == pieces.count() - 1) { + return next; + } + if (next && next->isDir()) { + return static_cast(next)->findByPath(pieces, index + 1); + } + return 0; + } + + void returnDirNodes(QList *store) + { + foreach(ArchiveNode *node, m_entries) { + if (node->isDir()) { + store->prepend(static_cast(node)); + static_cast(node)->returnDirNodes(store); + } + } + } + + void clear() + { + qDeleteAll(m_entries); + m_entries.clear(); + } + +private: + QList m_entries; +}; + +/** + * Helper functor used by qStableSort. + * + * It always sorts folders before files. + * + * @internal + */ +class ArchiveModelSorter +{ +public: + ArchiveModelSorter(int column, Qt::SortOrder order) + : m_sortColumn(column) + , m_sortOrder(order) + { + } + + virtual ~ArchiveModelSorter() + { + } + + inline bool operator()(const QPair &left, const QPair &right) const + { + if (m_sortOrder == Qt::AscendingOrder) { + return lessThan(left, right); + } else { + return !lessThan(left, right); + } + } + +protected: + bool lessThan(const QPair &left, const QPair &right) const + { + const ArchiveNode * const leftNode = left.first; + const ArchiveNode * const rightNode = right.first; + + // #234373: sort folders before files + if ((leftNode->isDir()) && (!rightNode->isDir())) { + return (m_sortOrder == Qt::AscendingOrder); + } else if ((!leftNode->isDir()) && (rightNode->isDir())) { + return !(m_sortOrder == Qt::AscendingOrder); + } + + const QVariant &leftEntry = leftNode->entry()[m_sortColumn]; + const QVariant &rightEntry = rightNode->entry()[m_sortColumn]; + + switch (m_sortColumn) { + case FileName: + return leftNode->name() < rightNode->name(); + case Size: + case CompressedSize: + return leftEntry.toInt() < rightEntry.toInt(); + default: + return leftEntry.toString() < rightEntry.toString(); + } + + // We should not get here. + Q_ASSERT(false); + return false; + } + +private: + int m_sortColumn; + Qt::SortOrder m_sortOrder; +}; + +int ArchiveNode::row() const +{ + if (parent()) { + return parent()->entries().indexOf(const_cast(this)); + } + return 0; +} + +ArchiveModel::ArchiveModel(const QString &dbusPathName, QObject *parent) + : QAbstractItemModel(parent) + , m_rootNode(new ArchiveDirNode(0, ArchiveEntry())) + , m_dbusPathName(dbusPathName) +{ +} + +ArchiveModel::~ArchiveModel() +{ + delete m_rootNode; + m_rootNode = 0; +} + +QVariant ArchiveModel::data(const QModelIndex &index, int role) const +{ + if (index.isValid()) { + ArchiveNode *node = static_cast(index.internalPointer()); + switch (role) { + case Qt::DisplayRole: { + //TODO: complete the columns + int columnId = m_showColumns.at(index.column()); + switch (columnId) { + case FileName: + return node->name(); + case Size: + if (node->isDir()) { + int dirs; + int files; + const int children = childCount(index, dirs, files); + return KIO::itemsSummaryString(children, files, dirs, 0, false); + } else if (node->entry().contains(Link)) { + return QVariant(); + } else { + return KIO::convertSize(node->entry()[ Size ].toULongLong()); + } + case CompressedSize: + if (node->isDir() || node->entry().contains(Link)) { + return QVariant(); + } else { + qulonglong compressedSize = node->entry()[ CompressedSize ].toULongLong(); + if (compressedSize != 0) { + return KIO::convertSize(compressedSize); + } else { + return QVariant(); + } + } + case Ratio: // TODO: Use node->entry()[Ratio] when available + if (node->isDir() || node->entry().contains(Link)) { + return QVariant(); + } else { + qulonglong compressedSize = node->entry()[ CompressedSize ].toULongLong(); + qulonglong size = node->entry()[ Size ].toULongLong(); + if (compressedSize == 0 || size == 0) { + return QVariant(); + } else { + int ratio = int(100 * ((double)size - compressedSize) / size); + return QString(QString::number(ratio) + QLatin1String( " %" )); + } + } + + case Timestamp: { + const QDateTime timeStamp = node->entry().value(Timestamp).toDateTime(); + return KGlobal::locale()->formatDateTime(timeStamp); + } + + default: + return node->entry().value(columnId); + } + break; + } + case Qt::DecorationRole: + if (index.column() == 0) { + return node->icon(); + } + return QVariant(); + case Qt::FontRole: { + QFont f; + f.setItalic(node->entry()[ IsPasswordProtected ].toBool()); + return f; + } + default: + return QVariant(); + } + } + return QVariant(); +} + +Qt::ItemFlags ArchiveModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index); + + if (index.isValid()) { + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | defaultFlags; + } + + return 0; +} + +QVariant ArchiveModel::headerData(int section, Qt::Orientation, int role) const +{ + if (role == Qt::DisplayRole) { + if (section >= m_showColumns.size()) { + kDebug() << "WEIRD: showColumns.size = " << m_showColumns.size() + << " and section = " << section; + return QVariant(); + } + + int columnId = m_showColumns.at(section); + + switch (columnId) { + case FileName: + return i18nc("Name of a file inside an archive", "Name"); + case Size: + return i18nc("Uncompressed size of a file inside an archive", "Size"); + case CompressedSize: + return i18nc("Compressed size of a file inside an archive", "Compressed"); + case Ratio: + return i18nc("Compression rate of file", "Rate"); + case Owner: + return i18nc("File's owner username", "Owner"); + case Group: + return i18nc("File's group", "Group"); + case Permissions: + return i18nc("File permissions", "Mode"); + case CRC: + return i18nc("CRC hash code", "CRC"); + case Method: + return i18nc("Compression method", "Method"); + case Version: + //TODO: what exactly is a file version? + return i18nc("File version", "Version"); + case Timestamp: + return i18nc("Timestamp", "Date"); + case Comment: + return i18nc("File comment", "Comment"); + default: + return i18nc("Unnamed column", "??"); + + } + } + return QVariant(); +} + +QModelIndex ArchiveModel::index(int row, int column, const QModelIndex &parent) const +{ + if (hasIndex(row, column, parent)) { + ArchiveDirNode *parentNode = parent.isValid() ? static_cast(parent.internalPointer()) : m_rootNode; + + Q_ASSERT(parentNode->isDir()); + + ArchiveNode *item = parentNode->entries().value(row, 0); + if (item) { + return createIndex(row, column, item); + } + } + + return QModelIndex(); +} + +QModelIndex ArchiveModel::parent(const QModelIndex &index) const +{ + if (index.isValid()) { + ArchiveNode *item = static_cast(index.internalPointer()); + Q_ASSERT(item); + if (item->parent() && (item->parent() != m_rootNode)) { + return createIndex(item->parent()->row(), 0, item->parent()); + } + } + return QModelIndex(); +} + +ArchiveEntry ArchiveModel::entryForIndex(const QModelIndex &index) +{ + if (index.isValid()) { + ArchiveNode *item = static_cast(index.internalPointer()); + Q_ASSERT(item); + return item->entry(); + } + return ArchiveEntry(); +} + +int ArchiveModel::childCount(const QModelIndex &index, int &dirs, int &files) const +{ + if (index.isValid()) { + dirs = files = 0; + ArchiveNode *item = static_cast(index.internalPointer()); + Q_ASSERT(item); + if (item->isDir()) { + const QList entries = static_cast(item)->entries(); + foreach(const ArchiveNode *node, entries) { + if (node->isDir()) { + dirs++; + } else { + files++; + } + } + return entries.count(); + } + return 0; + } + return -1; +} + +int ArchiveModel::rowCount(const QModelIndex &parent) const +{ + if (parent.column() <= 0) { + ArchiveNode *parentNode = parent.isValid() ? static_cast(parent.internalPointer()) : m_rootNode; + + if (parentNode && parentNode->isDir()) { + return static_cast(parentNode)->entries().count(); + } + } + return 0; +} + +int ArchiveModel::columnCount(const QModelIndex &parent) const +{ + return m_showColumns.size(); + if (parent.isValid()) { + return static_cast(parent.internalPointer())->entry().size(); + } +} + +void ArchiveModel::sort(int column, Qt::SortOrder order) +{ + if (m_showColumns.size() <= column) { + return; + } + + emit layoutAboutToBeChanged(); + + QList dirNodes; + m_rootNode->returnDirNodes(&dirNodes); + dirNodes.append(m_rootNode); + + const ArchiveModelSorter modelSorter(m_showColumns.at(column), order); + + foreach(ArchiveDirNode* dir, dirNodes) { + QVector < QPair > sorting(dir->entries().count()); + for (int i = 0; i < dir->entries().count(); ++i) { + ArchiveNode *item = dir->entries().at(i); + sorting[i].first = item; + sorting[i].second = i; + } + + qStableSort(sorting.begin(), sorting.end(), modelSorter); + + QModelIndexList fromIndexes; + QModelIndexList toIndexes; + for (int r = 0; r < sorting.count(); ++r) { + ArchiveNode *item = sorting.at(r).first; + toIndexes.append(createIndex(r, 0, item)); + fromIndexes.append(createIndex(sorting.at(r).second, 0, sorting.at(r).first)); + dir->setEntryAt(r, sorting.at(r).first); + } + + changePersistentIndexList(fromIndexes, toIndexes); + + emit dataChanged( + index(0, 0, indexForNode(dir)), + index(dir->entries().size() - 1, 0, indexForNode(dir))); + } + + emit layoutChanged(); +} + +Qt::DropActions ArchiveModel::supportedDropActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +QStringList ArchiveModel::mimeTypes() const +{ + QStringList types; + + // MIME types we accept for dragging (eg. Dolphin -> Ark). + types << QLatin1String("text/uri-list") + << QLatin1String("text/plain") + << QLatin1String("text/x-moz-url"); + + // MIME types we accept for dropping (eg. Ark -> Dolphin). + types << QLatin1String("application/x-kde-ark-dndextract-service") + << QLatin1String("application/x-kde-ark-dndextract-path"); + + return types; +} + +QMimeData *ArchiveModel::mimeData(const QModelIndexList &indexes) const +{ + Q_UNUSED(indexes) + + QMimeData *mimeData = new QMimeData; + mimeData->setData(QLatin1String("application/x-kde-ark-dndextract-service"), + QDBusConnection::sessionBus().baseService().toUtf8()); + mimeData->setData(QLatin1String("application/x-kde-ark-dndextract-path"), + m_dbusPathName.toUtf8()); + + return mimeData; +} + +bool ArchiveModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) +{ + Q_UNUSED(action) + Q_UNUSED(row) + Q_UNUSED(column) + Q_UNUSED(parent) + + if (!data->hasUrls()) { + return false; + } + + QStringList paths; + foreach(const QUrl &url, data->urls()) { + paths << url.toLocalFile(); + } + + //for now, this code is not used because adding files to paths inside the + //archive is not supported yet. need a solution for this later. + QString path; +#if 0 + if (parent.isValid()) { + QModelIndex droppedOnto = index(row, column, parent); + if (entryForIndex(droppedOnto).value(IsDirectory).toBool()) { + kDebug() << "Using entry"; + path = entryForIndex(droppedOnto).value(FileName).toString(); + } else { + path = entryForIndex(parent).value(FileName).toString(); + } + } + + kDebug() << "Dropped onto " << path; + +#endif + + emit droppedFiles(paths, path); + + return true; +} + +// For a rationale, see bugs #194241 and #241967 +QString ArchiveModel::cleanFileName(const QString& fileName) +{ + if ((fileName == QLatin1String("/")) || + (fileName == QLatin1String("."))) { // "." is present in ISO files + return QString(); + } else if (fileName.startsWith(QLatin1String("./"))) { + return fileName.mid(2); + } + + return fileName; +} + +ArchiveDirNode* ArchiveModel::parentFor(const ArchiveEntry& entry) +{ + QStringList pieces = entry[ FileName ].toString().split(QLatin1Char( '/' ), QString::SkipEmptyParts); + if (pieces.isEmpty()) { + return NULL; + } + pieces.removeLast(); + + if (s_previousMatch) { + //the number of path elements must be the same for the shortcut + //to work + if (s_previousPieces->count() == pieces.count()) { + bool equal = true; + + //make sure all the pieces match up + for (int i = 0; i < s_previousPieces->count(); ++i) { + if (s_previousPieces->at(i) != pieces.at(i)) { + equal = false; + break; + } + } + + //if match return it + if (equal) { + return static_cast(s_previousMatch); + } + } + } + + ArchiveDirNode *parent = m_rootNode; + + foreach(const QString &piece, pieces) { + ArchiveNode *node = parent->find(piece); + if (!node) { + ArchiveEntry e; + e[ FileName ] = (parent == m_rootNode) ? + piece : parent->entry()[ FileName ].toString() + QLatin1Char( '/' ) + piece; + e[ IsDirectory ] = true; + node = new ArchiveDirNode(parent, e); + insertNode(node); + } + if (!node->isDir()) { + ArchiveEntry e(node->entry()); + node = new ArchiveDirNode(parent, e); + //Maybe we have both a file and a directory of the same name + // We avoid removing previous entries unless necessary + insertNode(node); + } + parent = static_cast(node); + } + + s_previousMatch = parent; + *s_previousPieces = pieces; + + return parent; +} +QModelIndex ArchiveModel::indexForNode(ArchiveNode *node) +{ + Q_ASSERT(node); + if (node != m_rootNode) { + Q_ASSERT(node->parent()); + Q_ASSERT(node->parent()->isDir()); + return createIndex(node->row(), 0, node); + } + return QModelIndex(); +} + +void ArchiveModel::slotEntryRemoved(const QString & path) +{ + kDebug() << "Removed node at path " << path; + + const QString entryFileName(cleanFileName(path)); + if (entryFileName.isEmpty()) { + return; + } + + ArchiveNode *entry = m_rootNode->findByPath(entryFileName.split(QLatin1Char( '/' ), QString::SkipEmptyParts)); + if (entry) { + ArchiveDirNode *parent = entry->parent(); + QModelIndex index = indexForNode(entry); + + beginRemoveRows(indexForNode(parent), entry->row(), entry->row()); + + //delete parent->entries()[ entry->row() ]; + //parent->entries()[ entry->row() ] = 0; + parent->removeEntryAt(entry->row()); + + endRemoveRows(); + } else { + kDebug() << "Did not find the removed node"; + } +} + +void ArchiveModel::slotUserQuery(Kerfuffle::Query *query) +{ + query->execute(); +} + +void ArchiveModel::slotNewEntryFromSetArchive(const ArchiveEntry& entry) +{ + // we cache all entries that appear when opening a new archive + // so we can all them together once it's done, this is a huge + // performance improvement because we save from doing lots of + // begin/endInsertRows + m_newArchiveEntries.push_back(entry); +} + +void ArchiveModel::slotNewEntry(const ArchiveEntry& entry) +{ + newEntry(entry, NotifyViews); +} + +void ArchiveModel::newEntry(const ArchiveEntry& receivedEntry, InsertBehaviour behaviour) +{ + if (receivedEntry[FileName].toString().isEmpty()) { + kDebug() << "Weird, received empty entry (no filename) - skipping"; + return; + } + + //if there are no addidional columns registered, then have a look at the + //entry and populate some + if (m_showColumns.isEmpty()) { + //these are the columns we are interested in showing in the display + static const QList columnsForDisplay = + QList() + << FileName + << Size + << CompressedSize + << Permissions + << Owner + << Group + << Ratio + << CRC + << Method + << Version + << Timestamp + << Comment; + + QList toInsert; + + foreach(int column, columnsForDisplay) { + if (receivedEntry.contains(column)) { + toInsert << column; + } + } + beginInsertColumns(QModelIndex(), 0, toInsert.size() - 1); + m_showColumns << toInsert; + endInsertColumns(); + + kDebug() << "Show columns detected: " << m_showColumns; + } + + //make a copy + ArchiveEntry entry = receivedEntry; + + //#194241: Filenames such as "./file" should be displayed as "file" + //#241967: Entries called "/" should be ignored + QString entryFileName = cleanFileName(entry[FileName].toString()); + if (entryFileName.isEmpty()) { // The entry contains only "." or "./" + return; + } + entry[FileName] = entryFileName; + + /// 1. Skip already created nodes + if (m_rootNode) { + ArchiveNode *existing = m_rootNode->findByPath(entry[ FileName ].toString().split(QLatin1Char( '/' ))); + if (existing) { + kDebug() << "Refreshing entry for" << entry[FileName].toString(); + + // Multi-volume files are repeated at least in RAR archives. + // In that case, we need to sum the compressed size for each volume + qulonglong currentCompressedSize = existing->entry()[CompressedSize].toULongLong(); + entry[CompressedSize] = currentCompressedSize + entry[CompressedSize].toULongLong(); + + //TODO: benchmark whether it's a bad idea to reset the entry here. + existing->setEntry(entry); + return; + } + } + + /// 2. Find Parent Node, creating missing ArchiveDirNodes in the process + ArchiveDirNode *parent = parentFor(entry); + + /// 3. Create an ArchiveNode + QString name = entry[ FileName ].toString().split(QLatin1Char( '/' ), QString::SkipEmptyParts).last(); + ArchiveNode *node = parent->find(name); + if (node) { + node->setEntry(entry); + } else { + if (entry[ FileName ].toString().endsWith(QLatin1Char( '/' )) || (entry.contains(IsDirectory) && entry[ IsDirectory ].toBool())) { + node = new ArchiveDirNode(parent, entry); + } else { + node = new ArchiveNode(parent, entry); + } + insertNode(node, behaviour); + } +} + +void ArchiveModel::slotLoadingFinished(KJob *job) +{ + //kDebug() << entry; + foreach(const ArchiveEntry &entry, m_newArchiveEntries) { + newEntry(entry, DoNotNotifyViews); + } + reset(); + m_newArchiveEntries.clear(); + + emit loadingFinished(job); +} + +void ArchiveModel::insertNode(ArchiveNode *node, InsertBehaviour behaviour) +{ + Q_ASSERT(node); + ArchiveDirNode *parent = node->parent(); + Q_ASSERT(parent); + if (behaviour == NotifyViews) { + beginInsertRows(indexForNode(parent), parent->entries().count(), parent->entries().count()); + } + parent->appendEntry(node); + if (behaviour == NotifyViews) { + endInsertRows(); + } +} + +Kerfuffle::Archive* ArchiveModel::archive() const +{ + return m_archive.data(); +} + +KJob* ArchiveModel::setArchive(Kerfuffle::Archive *archive) +{ + m_archive.reset(archive); + + m_rootNode->clear(); + s_previousMatch = 0; + s_previousPieces->clear(); + + Kerfuffle::ListJob *job = NULL; + + m_newArchiveEntries.clear(); + if (m_archive) { + job = m_archive->list(); // TODO: call "open" or "create"? + + connect(job, SIGNAL(newEntry(ArchiveEntry)), + this, SLOT(slotNewEntryFromSetArchive(ArchiveEntry))); + + connect(job, SIGNAL(result(KJob*)), + this, SLOT(slotLoadingFinished(KJob*))); + + connect(job, SIGNAL(userQuery(Kerfuffle::Query*)), + this, SLOT(slotUserQuery(Kerfuffle::Query*))); + + emit loadingStarted(); + + // TODO: make sure if it's ok to not have calls to beginRemoveColumns here + m_showColumns.clear(); + } + reset(); + return job; +} + +ExtractJob* ArchiveModel::extractFile(const QVariant& fileName, const QString & destinationDir, const Kerfuffle::ExtractionOptions options) const +{ + QList files; + files << fileName; + return extractFiles(files, destinationDir, options); +} + +ExtractJob* ArchiveModel::extractFiles(const QList& files, const QString & destinationDir, const Kerfuffle::ExtractionOptions options) const +{ + Q_ASSERT(m_archive); + ExtractJob *newJob = m_archive->copyFiles(files, destinationDir, options); + connect(newJob, SIGNAL(userQuery(Kerfuffle::Query*)), + this, SLOT(slotUserQuery(Kerfuffle::Query*))); + return newJob; +} + +AddJob* ArchiveModel::addFiles(const QStringList & filenames, const CompressionOptions& options) +{ + if (!m_archive) { + return NULL; + } + + if (!m_archive->isReadOnly()) { + AddJob *job = m_archive->addFiles(filenames, options); + connect(job, SIGNAL(newEntry(ArchiveEntry)), + this, SLOT(slotNewEntry(ArchiveEntry))); + connect(job, SIGNAL(userQuery(Kerfuffle::Query*)), + this, SLOT(slotUserQuery(Kerfuffle::Query*))); + + + return job; + } + return 0; +} + +DeleteJob* ArchiveModel::deleteFiles(const QList & files) +{ + Q_ASSERT(m_archive); + if (!m_archive->isReadOnly()) { + DeleteJob *job = m_archive->deleteFiles(files); + connect(job, SIGNAL(entryRemoved(QString)), + this, SLOT(slotEntryRemoved(QString))); + + connect(job, SIGNAL(finished(KJob*)), + this, SLOT(slotCleanupEmptyDirs())); + + connect(job, SIGNAL(userQuery(Kerfuffle::Query*)), + this, SLOT(slotUserQuery(Kerfuffle::Query*))); + return job; + } + return 0; +} + +void ArchiveModel::slotCleanupEmptyDirs() +{ + kDebug(); + QList queue; + QList nodesToDelete; + + //add root nodes + for (int i = 0; i < rowCount(); ++i) { + queue.append(QPersistentModelIndex(index(i, 0))); + } + + //breadth-first traverse + while (!queue.isEmpty()) { + QPersistentModelIndex node = queue.takeFirst(); + ArchiveEntry entry = entryForIndex(node); + //kDebug() << "Trying " << entry[FileName].toString(); + + if (!hasChildren(node)) { + if (!entry.contains(InternalID)) { + nodesToDelete << node; + } + } else { + for (int i = 0; i < rowCount(node); ++i) { + queue.append(QPersistentModelIndex(index(i, 0, node))); + } + } + } + + foreach(const QPersistentModelIndex& node, nodesToDelete) { + ArchiveNode *rawNode = static_cast(node.internalPointer()); + kDebug() << "Delete with parent entries " << rawNode->parent()->entries() << " and row " << rawNode->row(); + beginRemoveRows(parent(node), rawNode->row(), rawNode->row()); + rawNode->parent()->removeEntryAt(rawNode->row()); + endRemoveRows(); + //kDebug() << "Removed entry " << entry[FileName].toString(); + } +} + +#include "archivemodel.moc" diff --git a/ark/part/archivemodel.h b/ark/part/archivemodel.h new file mode 100644 index 00000000..7f8c527f --- /dev/null +++ b/ark/part/archivemodel.h @@ -0,0 +1,126 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2007 Henrique Pinto + * Copyright (C) 2008-2009 Harald Hvaal + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef ARCHIVEMODEL_H +#define ARCHIVEMODEL_H + +#include +#include + +#include +#include "kerfuffle/archive.h" + +using Kerfuffle::ArchiveEntry; + +namespace Kerfuffle +{ + class Query; +} + +class ArchiveNode; +class ArchiveDirNode; + +class ArchiveModel: public QAbstractItemModel +{ + Q_OBJECT +public: + ArchiveModel(const QString &dbusPathName, QObject *parent = 0); + ~ArchiveModel(); + + QVariant data(const QModelIndex &index, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + + //drag and drop related + virtual Qt::DropActions supportedDropActions() const; + virtual QStringList mimeTypes() const; + virtual QMimeData * mimeData(const QModelIndexList & indexes) const; + + virtual bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent); + + KJob* setArchive(Kerfuffle::Archive *archive); + Kerfuffle::Archive *archive() const; + + Kerfuffle::ArchiveEntry entryForIndex(const QModelIndex &index); + int childCount(const QModelIndex &index, int &dirs, int &files) const; + + Kerfuffle::ExtractJob* extractFile(const QVariant& fileName, const QString & destinationDir, const Kerfuffle::ExtractionOptions options = Kerfuffle::ExtractionOptions()) const; + Kerfuffle::ExtractJob* extractFiles(const QList& files, const QString & destinationDir, const Kerfuffle::ExtractionOptions options = Kerfuffle::ExtractionOptions()) const; + + Kerfuffle::AddJob* addFiles(const QStringList & paths, const Kerfuffle::CompressionOptions& options = Kerfuffle::CompressionOptions()); + Kerfuffle::DeleteJob* deleteFiles(const QList & files); + +signals: + void loadingStarted(); + void loadingFinished(KJob *); + void extractionFinished(bool success); + void error(const QString& error, const QString& details); + void droppedFiles(const QStringList& files, const QString& path = QString()); + +private slots: + void slotNewEntryFromSetArchive(const ArchiveEntry& entry); + void slotNewEntry(const ArchiveEntry& entry); + void slotLoadingFinished(KJob *job); + void slotEntryRemoved(const QString & path); + void slotUserQuery(Kerfuffle::Query *query); + void slotCleanupEmptyDirs(); + +private: + /** + * Strips file names that start with './'. + * + * For more information, see bug 194241. + * + * @param fileName The file name that will be stripped. + * + * @return @p fileName without the leading './' + */ + QString cleanFileName(const QString& fileName); + + ArchiveDirNode* parentFor(const Kerfuffle::ArchiveEntry& entry); + QModelIndex indexForNode(ArchiveNode *node); + static bool compareAscending(const QModelIndex& a, const QModelIndex& b); + static bool compareDescending(const QModelIndex& a, const QModelIndex& b); + /** + * Insert the node @p node into the model, ensuring all views are notified + * of the change. + */ + enum InsertBehaviour { NotifyViews, DoNotNotifyViews }; + void insertNode(ArchiveNode *node, InsertBehaviour behaviour = NotifyViews); + void newEntry(const Kerfuffle::ArchiveEntry& entry, InsertBehaviour behaviour); + + QList m_newArchiveEntries; // holds entries from opening a new archive until it's totally open + QList m_showColumns; + QScopedPointer m_archive; + ArchiveDirNode *m_rootNode; + + QString m_dbusPathName; +}; + +#endif // ARCHIVEMODEL_H diff --git a/ark/part/archiveview.cpp b/ark/part/archiveview.cpp new file mode 100644 index 00000000..6b9918df --- /dev/null +++ b/ark/part/archiveview.cpp @@ -0,0 +1,142 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2008-2009 Harald Hvaal + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "archiveview.h" + +#include +#include + +#include +#include +#include +#include + +ArchiveView::ArchiveView(QWidget *parent) + : QTreeView(parent) + , m_mouseButtons(Qt::NoButton) +{ + connect(this, SIGNAL(pressed(QModelIndex)), + SLOT(updateMouseButtons())); + connect(this, SIGNAL(clicked(QModelIndex)), + SLOT(slotClicked(QModelIndex))); + connect(this, SIGNAL(doubleClicked(QModelIndex)), + SLOT(slotDoubleClicked(QModelIndex))); +} + +// FIXME: this is a workaround taken from Dolphin until QTBUG-1067 is resolved +void ArchiveView::updateMouseButtons() +{ + m_mouseButtons = QApplication::mouseButtons(); +} + +void ArchiveView::slotClicked(const QModelIndex& index) +{ + if (KGlobalSettings::singleClick()) { + if (m_mouseButtons != Qt::LeftButton) { // FIXME: see QTBUG-1067 + return; + } + + // If the user is pressing shift or control, more than one item is being selected + const Qt::KeyboardModifiers modifier = QApplication::keyboardModifiers(); + if ((modifier & Qt::ShiftModifier) || (modifier & Qt::ControlModifier)) { + return; + } + + emit itemTriggered(index); + } +} + +void ArchiveView::slotDoubleClicked(const QModelIndex& index) +{ + if (!KGlobalSettings::singleClick()) { + emit itemTriggered(index); + } +} + +void ArchiveView::setModel(QAbstractItemModel *model) +{ + kDebug(); + QTreeView::setModel(model); + setSelectionMode(QAbstractItemView::ExtendedSelection); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setAlternatingRowColors(true); + setAnimated(true); + setAllColumnsShowFocus(true); + setSortingEnabled(true); + + //drag and drop + setDragEnabled(true); + setAcceptDrops(true); + setDropIndicatorShown(true); + setDragDropMode(QAbstractItemView::DragDrop); +} + +void ArchiveView::startDrag(Qt::DropActions supportedActions) +{ + //only start the drag if it's over the filename column. this allows dragging selection in + //tree/detail view + if (currentIndex().column() != 0) { + return; + } + + kDebug() << "Singling out the current selection..."; + selectionModel()->setCurrentIndex(currentIndex(), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + QTreeView::startDrag(supportedActions); +} + + +void ArchiveView::dragEnterEvent(QDragEnterEvent * event) +{ + //TODO: if no model, trigger some mechanism to create one automatically! + kDebug() << event; + + if (event->source() == this) { + //we don't support internal drops yet. + return; + } + + QTreeView::dragEnterEvent(event); +} + +void ArchiveView::dropEvent(QDropEvent * event) +{ + kDebug() << event; + + if (event->source() == this) { + //we don't support internal drops yet. + return; + } + + QTreeView::dropEvent(event); +} + +void ArchiveView::dragMoveEvent(QDragMoveEvent * event) +{ + if (event->source() == this) { + //we don't support internal drops yet. + return; + } + + QTreeView::dragMoveEvent(event); + if (event->mimeData()->hasFormat(QLatin1String("text/uri-list"))) { + event->acceptProposedAction(); + } +} diff --git a/ark/part/archiveview.h b/ark/part/archiveview.h new file mode 100644 index 00000000..daac59b1 --- /dev/null +++ b/ark/part/archiveview.h @@ -0,0 +1,54 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2008 Harald Hvaal + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ARCHIVEVIEW_H +#define ARCHIVEVIEW_H + +#include + +class ArchiveView : public QTreeView +{ + Q_OBJECT + +public: + ArchiveView(QWidget *parent = 0); + virtual void dragEnterEvent(class QDragEnterEvent * event); + virtual void dropEvent(class QDropEvent * event); + virtual void dragMoveEvent(class QDragMoveEvent * event); + virtual void startDrag(Qt::DropActions supportedActions); + + void setModel(QAbstractItemModel *model); + +protected slots: + void slotClicked(const QModelIndex & index); + void slotDoubleClicked(const QModelIndex & index); + +private slots: + void updateMouseButtons(); + +signals: + void itemTriggered(const QModelIndex & index); + +private: + Qt::MouseButtons m_mouseButtons; // FIXME: workaround until QTBUG-1067 is resolved +}; + +#endif /* ARCHIVEVIEW_H */ diff --git a/ark/part/ark_part.desktop.cmake b/ark/part/ark_part.desktop.cmake new file mode 100644 index 00000000..84409bc0 --- /dev/null +++ b/ark/part/ark_part.desktop.cmake @@ -0,0 +1,152 @@ +[Desktop Entry] +Icon=ark +Name=Archiver +Name[af]=Argifeerder +Name[ar]=المؤرشف +Name[ast]=Archivador +Name[bg]=Архиватор +Name[br]=Dieller +Name[bs]=Arhivar +Name[ca]=Arxivador +Name[ca@valencia]=Arxivador +Name[cs]=Archivátor +Name[cy]=Archifydd +Name[da]=Arkivbehandler +Name[de]=Archivprogramm +Name[el]=Πρόγραμμα αρχειοθέτησης +Name[en_GB]=Archiver +Name[eo]=Arkivilo +Name[es]=Archivador +Name[et]=Arhiveerija +Name[eu]=Artxibatzeko programa +Name[fa]=بایگانی‌کننده +Name[fi]=Pakkausohjelma +Name[fr]=Archiveur +Name[ga]=Archiver +Name[gl]=Arquivador +Name[he]=מנהל הארכיונים +Name[hne]=अभिलेखक +Name[hr]=Arhiver +Name[hu]=Ark fájltömörítő +Name[ia]=Archivator +Name[id]=Pengarsip +Name[is]=Skráasafnari +Name[it]=Utilità di archiviazione +Name[ja]=アーカイバ +Name[kk]=Архивтегіш +Name[km]=កម្មវិធីប័ណ្ណសារ +Name[ko]=Archiver +Name[lt]=Archyvatorius +Name[lv]=Arhivātors +Name[mk]=Архивер +Name[mr]=संग्रहकर्ता +Name[ms]=Pengarkib +Name[nb]=Arkivbehandler +Name[nds]=Archivater +Name[ne]=सङ्ग्रहक +Name[nl]=Archiefgereedschap +Name[nn]=Arkivprogram +Name[pa]=ਅਕਾਇਵਰ +Name[pl]=Ark +Name[pt]=Ark +Name[pt_BR]=Arquivador +Name[ro]=Arhivator +Name[ru]=Архиватор +Name[sk]=Archivačný nástroj +Name[sl]=Pakirnik +Name[sq]=Arkivues +Name[sr]=Архивар +Name[sr@ijekavian]=Архивар +Name[sr@ijekavianlatin]=Arhivar +Name[sr@latin]=Arhivar +Name[sv]=Arkiverare +Name[ta]=காப்பகம் உருவாக்கி +Name[tg]=Бойгонигар +Name[th]=ตัวสร้างแฟ้มจัดเก็บ +Name[tr]=Arşivleyici +Name[uk]=Архіватор +Name[uz]=Arxivlagich +Name[uz@cyrillic]=Архивлагич +Name[vi]=Trình Lưu Trữ +Name[wa]=Årtchiveu +Name[xh]=Umenzi woshicilelo lukawonke-wonke noxwebhu lweMbali +Name[x-test]=xxArchiverxx +Name[zh_CN]=压缩存档工具 +Name[zh_TW]=壓縮檔處理器 +Comment=Archive Handling Tool +Comment[af]=Argief Handtering Program +Comment[ar]=أداة التعامل مع الملفات المضغوطة +Comment[ast]=Ferramienta pa remanar archivadores +Comment[bg]=Работа с архиви +Comment[br]=Ostilh merañ an dielloù +Comment[bs]=Alatka za rukovanje arhivama +Comment[ca]=Eina per a treballar amb arxius +Comment[ca@valencia]=Eina per a treballar amb arxius +Comment[cs]=Program pro práci s archivy +Comment[cy]=Erfyn Triniaeth Archif +Comment[da]=Arkivbehandlingsværktøj +Comment[de]=Archiv-Verwaltung +Comment[el]=Εργαλείο χειρισμού αρχειοθηκών +Comment[en_GB]=Archive Handling Tool +Comment[eo]=Administrilo por arĥivoj +Comment[es]=Herramienta de gestión de archivos +Comment[et]=Arhiivide haldamise rakendus +Comment[eu]=Artxiboak kudeatzeko tresna +Comment[fa]=ابزار گرداندن بایگانی +Comment[fi]=Pakkausohjelma +Comment[fr]=Outil de manipulation d'archives +Comment[ga]=Uirlis Láimhseála Cartlainne +Comment[gl]=Utilidade de manexo de arquivos +Comment[he]=כלי לניהול ארכיונים +Comment[hne]=अभिलेख संभाल औजार +Comment[hr]=Uslužni program za arhiviranje +Comment[hu]=Tömörítőprogram +Comment[ia]=Instrumento de manear archivo +Comment[id]=Perkakas Penanganan Arsip +Comment[is]=Vinna með safnskrár +Comment[it]=Gestione degli archivi +Comment[ja]=アーカイブを操作するツール +Comment[kk]=Архивпен айналысу құралы +Comment[km]=ឧបករណ៍​គ្រប់គ្រង​ប័ណ្ណសារ +Comment[ko]=압축 파일 도구 +Comment[lt]=Archyvo valdymo priemonė +Comment[lv]=Arhīvu apstrādes rīks +Comment[mk]=Алатка за справување со архивирани датотеки +Comment[mr]=संग्रह हाताळणारे साधन +Comment[ms]=Alatan Pengendalian Arkib +Comment[nb]=Arkivbehandlingsverktøy +Comment[nds]=En Warktüüch för de Archievpleeg +Comment[ne]=ह्यान्डलिङ उपकरण सङ्ग्रह गर्नुहोस् +Comment[nl]=Hulpprogramma voor het beheren van archieven +Comment[nn]=Verktøy for arkivhandsaming +Comment[pa]=ਅਕਾਇਵ ਹੈਡਲਿੰਗ ਟੂਲ +Comment[pl]=Program obsługi archiwów +Comment[pt]=Programa de gestão de arquivos +Comment[pt_BR]=Ferramenta de manipulação de arquivos +Comment[ro]=Utilitar de manipulare arhive +Comment[ru]=Программа работы с архивами +Comment[sk]=Nástroj na prácu s archívmi +Comment[sl]=Orodje za ravnanje z arhivi +Comment[sq]=Mjet Për Përpunimin e Arkivave +Comment[sr]=Алатка за руковање архивама +Comment[sr@ijekavian]=Алатка за руковање архивама +Comment[sr@ijekavianlatin]=Alatka za rukovanje arhivama +Comment[sr@latin]=Alatka za rukovanje arhivama +Comment[sv]=Verktyg för att hantera filarkiv +Comment[ta]=காப்பகத்தை கையாளும் கருவி +Comment[tg]=Асбобҳои Дасткории Бойгонӣ +Comment[th]=เครื่องมือจัดการแฟ้มจัดเก็บ +Comment[tr]=Arşiv İşleme Aracı +Comment[uk]=Засіб роботи з архівами +Comment[uz]=Arxiv uchun vosita +Comment[uz@cyrillic]=Архив учун восита +Comment[vi]=Công Cụ Xử Lý Các Tập Tin Nén +Comment[wa]=Usteye po-z apougnî les årtchives +Comment[xh]=Isixhobo sokuphatha i Archive +Comment[x-test]=xxArchive Handling Toolxx +Comment[zh_CN]=压缩归档处理工具 +Comment[zh_TW]=壓縮檔處理工具 +X-KDE-ServiceTypes=KParts/ReadOnlyPart,Browser/View +X-KDE-Library=arkpart +Type=Service +MimeType=@SUPPORTED_ARK_MIMETYPES@ diff --git a/ark/part/ark_part.rc b/ark/part/ark_part.rc new file mode 100644 index 00000000..044c11a5 --- /dev/null +++ b/ark/part/ark_part.rc @@ -0,0 +1,28 @@ + + + + + &File + + + + &Action + + + + + + + + &Settings + + + + + + + + + + + diff --git a/ark/part/arkviewer.cpp b/ark/part/arkviewer.cpp new file mode 100644 index 00000000..13a9f3c8 --- /dev/null +++ b/ark/part/arkviewer.cpp @@ -0,0 +1,266 @@ +/* + * ark: A program for modifying archives via a GUI. + * + * Copyright (C) 2004-2008 Henrique Pinto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "arkviewer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +ArkViewer::ArkViewer(QWidget * parent, Qt::WFlags flags) + : KDialog(parent, flags) +{ + setButtons(Close); + m_widget = new KVBox(this); + m_widget->layout()->setSpacing(10); + + setMainWidget(m_widget); + + connect(this, SIGNAL(finished()), SLOT(dialogClosed())); +} + +ArkViewer::~ArkViewer() +{ +} + +void ArkViewer::dialogClosed() +{ + KConfigGroup conf = KGlobal::config()->group("Viewer"); + saveDialogSize(conf); + + if (m_part) { + KProgressDialog progressDialog + (this, i18n("Closing preview"), + i18n("Please wait while the preview is being closed...")); + + progressDialog.setMinimumDuration(500); + progressDialog.setModal(true); + progressDialog.setAllowCancel(false); + progressDialog.progressBar()->setRange(0, 0); + + // #261785: this preview dialog is not modal, so we need to delete + // the previewed file ourselves when the dialog is closed; + // we used to remove it at the end of ArkViewer::view() when + // QDialog::exec() was called instead of QDialog::show(). + const QString previewedFilePath(m_part.data()->url().pathOrUrl()); + + m_part.data()->closeUrl(); + + if (!previewedFilePath.isEmpty()) { + QFile::remove(previewedFilePath); + } + } +} + +void ArkViewer::view(const QString& fileName, QWidget *parent) +{ + KMimeType::Ptr mimeType = KMimeType::findByPath(fileName); + kDebug() << "MIME type" << mimeType->name(); + KService::Ptr viewer = ArkViewer::getViewer(mimeType); + + const bool needsExternalViewer = (!viewer.isNull() && + !viewer->hasServiceType(QLatin1String("KParts/ReadOnlyPart"))); + if (needsExternalViewer) { + // We have already resolved the MIME type and the service above. + // So there is no point in using KRun::runUrl() which would need + // to do the same again. + + const KUrl::List fileUrlList = KUrl(fileName); + // The last argument (tempFiles) set to true means that the temporary + // file will be removed when the viewer application exits. + KRun::run(*viewer, fileUrlList, parent, true); + return; + } + + bool viewInInternalViewer = true; + if (viewer.isNull()) { + // No internal viewer available for the file. Ask the user if it + // should be previewed as text/plain. + + int response; + if (!mimeType->isDefault()) { + // File has a defined MIME type, and not the default + // application/octet-stream. So it could be viewable as + // plain text, ask the user. + response = KMessageBox::warningContinueCancel(parent, + i18n("The internal viewer cannot preview this type of file(%1).Do you want to try to view it as plain text?", mimeType->name()), + i18nc("@title:window", "Cannot Preview File"), + KGuiItem(i18nc("@action:button", "Preview as Text"), KIcon(QLatin1String("text-plain"))), + KStandardGuiItem::cancel(), + QString(QLatin1String("PreviewAsText_%1")).arg(mimeType->name())); + } + else { + // No defined MIME type, or the default application/octet-stream. + // There is still a possibility that it could be viewable as plain + // text, so ask the user. Not the same as the message/question + // above, because the wording and default are different. + response = KMessageBox::warningContinueCancel(parent, + i18n("The internal viewer cannot preview this unknown type of file.Do you want to try to view it as plain text?"), + i18nc("@title:window", "Cannot Preview File"), + KGuiItem(i18nc("@action:button", "Preview as Text"), KIcon(QLatin1String("text-plain"))), + KStandardGuiItem::cancel(), + QString(), + KMessageBox::Dangerous); + } + + if (response == KMessageBox::Cancel) { + viewInInternalViewer = false; + } + else { // set for viewer later + mimeType = KMimeType::mimeType(QLatin1String("text/plain")); + } + } + + if (viewInInternalViewer) { + ArkViewer *internalViewer = new ArkViewer(parent, Qt::Window); + if (internalViewer->viewInInternalViewer(fileName, mimeType)) { + internalViewer->show(); + // The internal viewer is showing the file, and will + // remove the temporary file in dialogClosed(). So there + // is no more to do here. + return; + } + else { + KMessageBox::sorry(parent, i18n("The internal viewer cannot preview this file.")); + delete internalViewer; + } + } + + // Only get here if there is no internal viewer available or could be + // used for the file, and no external viewer was opened. Nothing can be + // done with the temporary file, so remove it now. + QFile::remove(fileName); +} + +void ArkViewer::keyPressEvent(QKeyEvent *event) +{ + KPushButton *defButton = button(defaultButton()); + + // Only handle the event the usual way if the default button has focus + // Otherwise, pressing enter on KatePart still closes the dialog, for example. + if ((defButton) && (defButton->hasFocus())) { + KDialog::keyPressEvent(event); + } + + event->accept(); +} + +// This sets the default size of the dialog. It will only take effect in the case +// where there is no saved size in the config file - it sets the default values +// for KDialog::restoreDialogSize(). +QSize ArkViewer::sizeHint() const +{ + return QSize(560, 400); +} + +bool ArkViewer::viewInInternalViewer(const QString& fileName, const KMimeType::Ptr& mimeType) +{ + const KUrl fileUrl(fileName); + + setCaption(fileUrl.fileName()); + restoreDialogSize(KGlobal::config()->group("Viewer")); + + QFrame *header = new QFrame(m_widget); + QHBoxLayout *headerLayout = new QHBoxLayout(header); + + QLabel *iconLabel = new QLabel(header); + headerLayout->addWidget(iconLabel); + iconLabel->setPixmap(KIconLoader::global()->loadMimeTypeIcon(mimeType->iconName(), KIconLoader::Desktop)); + iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); + + KVBox *headerRight = new KVBox(header); + headerLayout->addWidget(headerRight); + new QLabel(QString(QLatin1String( "%1" )) + .arg(fileUrl.fileName()), headerRight + ); + new QLabel(mimeType->comment(), headerRight); + + header->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + + m_part = KMimeTypeTrader::self()->createPartInstanceFromQuery(mimeType->name(), + m_widget, + this); + + if (!m_part.data()) { + return false; + } + + if (m_part.data()->browserExtension()) { + connect(m_part.data()->browserExtension(), + SIGNAL(openUrlRequestDelayed(KUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)), + SLOT(slotOpenUrlRequestDelayed(KUrl,KParts::OpenUrlArguments,KParts::BrowserArguments))); + } + + m_part.data()->openUrl(fileUrl); + + return true; +} + +void ArkViewer::slotOpenUrlRequestDelayed(const KUrl& url, const KParts::OpenUrlArguments& arguments, const KParts::BrowserArguments& browserArguments) +{ + kDebug() << "Opening URL: " << url; + + Q_UNUSED(arguments) + Q_UNUSED(browserArguments) + + KRun *runner = new KRun(url, 0, 0, false); + runner->setRunExecutables(false); +} + +KService::Ptr ArkViewer::getViewer(const KMimeType::Ptr &mimeType) +{ + // No point in even trying to find anything for application/octet-stream + if (mimeType->isDefault()) { + return KService::Ptr(); + } + + // Try to get a read-only kpart for the internal viewer + KService::List offers = KMimeTypeTrader::self()->query(mimeType->name(), QString::fromLatin1("KParts/ReadOnlyPart")); + + // If we can't find a kpart, try to get an external application + if (offers.size() == 0) { + offers = KMimeTypeTrader::self()->query(mimeType->name(), QString::fromLatin1("Application")); + } + + if (offers.size() > 0) { + return offers.first(); + } else { + return KService::Ptr(); + } +} + + +#include "arkviewer.moc" diff --git a/ark/part/arkviewer.h b/ark/part/arkviewer.h new file mode 100644 index 00000000..bb41472e --- /dev/null +++ b/ark/part/arkviewer.h @@ -0,0 +1,63 @@ +/* + * ark: A program for modifying archives via a GUI. + * + * Copyright (C) 2004-2008, Henrique Pinto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ARKVIEWER_H +#define ARKVIEWER_H + +#include +#include +#include +#include +#include + +#include + +class ArkViewer : public KDialog +{ + Q_OBJECT + +public: + virtual ~ArkViewer(); + virtual QSize sizeHint() const; + + static void view(const QString& fileName, QWidget* parent = 0); + +protected: + virtual void keyPressEvent(QKeyEvent *event); + +protected slots: + void slotOpenUrlRequestDelayed(const KUrl& url, const KParts::OpenUrlArguments& arguments, const KParts::BrowserArguments& browserArguments); + +private slots: + void dialogClosed(); + +private: + explicit ArkViewer(QWidget* parent = 0, Qt::WFlags flags = 0); + + static KService::Ptr getViewer(const KMimeType::Ptr& mimeType); + bool viewInInternalViewer(const QString& fileName, const KMimeType::Ptr& mimeType); + + QWeakPointer m_part; + QWidget *m_widget; +}; + +#endif // ARKVIEWER_H + diff --git a/ark/part/dnddbusinterface.xml b/ark/part/dnddbusinterface.xml new file mode 100644 index 00000000..66fb1fff --- /dev/null +++ b/ark/part/dnddbusinterface.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/ark/part/infopanel.cpp b/ark/part/infopanel.cpp new file mode 100644 index 00000000..433652ed --- /dev/null +++ b/ark/part/infopanel.cpp @@ -0,0 +1,213 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2007 Henrique Pinto + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "infopanel.h" +#include "kerfuffle/archive.h" + +#include +#include +#include + +#include +#include +#include +#include + +using namespace Kerfuffle; + +static QPixmap getMimeIcon(const QString& mimeName) +{ + return KIconLoader::global()->loadMimeTypeIcon(mimeName, KIconLoader::Desktop, KIconLoader::SizeHuge); +} + +InfoPanel::InfoPanel(ArchiveModel *model, QWidget *parent) + : QFrame(parent), m_model(model) +{ + setupUi(this); + + // Make the file name font bigger than the rest + QFont fnt = fileName->font(); + if (fnt.pointSize() > -1) { + fnt.setPointSize(fnt.pointSize() + 1); + } else { + fnt.setPixelSize(fnt.pixelSize() + 3); + } + fileName->setFont(fnt); + + updateWithDefaults(); +} + +InfoPanel::~InfoPanel() +{ +} + +void InfoPanel::updateWithDefaults() +{ + iconLabel->setPixmap(KIconLoader::global()->loadIcon(QLatin1String( "utilities-file-archiver" ), KIconLoader::Desktop, KIconLoader::SizeHuge)); + + const QString currentFileName = prettyFileName(); + + if (currentFileName.isEmpty()) { + fileName->setText(i18n("No archive loaded")); + } else { + fileName->setText(currentFileName); + } + + additionalInfo->setText(QString()); + hideMetaData(); + hideActions(); +} + +QString InfoPanel::prettyFileName() const +{ + if (m_prettyFileName.isEmpty()) { + if (m_model->archive()) { + QFileInfo fileInfo(m_model->archive()->fileName()); + return fileInfo.fileName(); + } + } + + return m_prettyFileName; +} + +void InfoPanel::setPrettyFileName(const QString& fileName) +{ + m_prettyFileName = fileName; +} + +void InfoPanel::setIndex(const QModelIndex& index) +{ + if (!index.isValid()) { + updateWithDefaults(); + } else { + const ArchiveEntry& entry = m_model->entryForIndex(index); + + KMimeType::Ptr mimeType; + + if (entry[ IsDirectory ].toBool()) { + mimeType = KMimeType::mimeType(QLatin1String( "inode/directory" )); + } else { + mimeType = KMimeType::findByPath(entry[ FileName ].toString(), 0, true); + } + + iconLabel->setPixmap(getMimeIcon(mimeType->iconName())); + if (entry[ IsDirectory ].toBool()) { + int dirs; + int files; + const int children = m_model->childCount(index, dirs, files); + additionalInfo->setText(KIO::itemsSummaryString(children, files, dirs, 0, false)); + } else if (entry.contains(Link)) { + additionalInfo->setText(i18n("Symbolic Link")); + } else { + if (entry.contains(Size)) { + additionalInfo->setText(KIO::convertSize(entry[ Size ].toULongLong())); + } else { + additionalInfo->setText(i18n("Unknown size")); + + } + } + + const QStringList nameParts = entry[ FileName ].toString().split(QLatin1Char( '/' ), QString::SkipEmptyParts); + const QString name = (nameParts.count() > 0) ? nameParts.last() : entry[ FileName ].toString(); + fileName->setText(name); + + metadataLabel->setText(metadataTextFor(index)); + showMetaData(); + } +} + +void InfoPanel::setIndexes(const QModelIndexList &list) +{ + if (list.size() == 0) { + setIndex(QModelIndex()); + } else if (list.size() == 1) { + setIndex(list[ 0 ]); + } else { + iconLabel->setPixmap(KIconLoader::global()->loadIcon(QLatin1String( "utilities-file-archiver" ), KIconLoader::Desktop, KIconLoader::SizeHuge)); + fileName->setText(i18np("One file selected", "%1 files selected", list.size())); + quint64 totalSize = 0; + foreach(const QModelIndex& index, list) { + const ArchiveEntry& entry = m_model->entryForIndex(index); + totalSize += entry[ Size ].toULongLong(); + } + additionalInfo->setText(KIO::convertSize(totalSize)); + hideMetaData(); + } +} + +void InfoPanel::showMetaData() +{ + firstSeparator->show(); + metadataLabel->show(); +} + +void InfoPanel::hideMetaData() +{ + firstSeparator->hide(); + metadataLabel->hide(); +} + +void InfoPanel::showActions() +{ + secondSeparator->show(); + actionsLabel->show(); +} + +void InfoPanel::hideActions() +{ + secondSeparator->hide(); + actionsLabel->hide(); +} + +QString InfoPanel::metadataTextFor(const QModelIndex &index) +{ + const ArchiveEntry& entry = m_model->entryForIndex(index); + QString text; + + KMimeType::Ptr mimeType; + + if (entry[ IsDirectory ].toBool()) { + mimeType = KMimeType::mimeType(QLatin1String( "inode/directory" )); + } else { + mimeType = KMimeType::findByPath(entry[ FileName ].toString(), 0, true); + } + + text += i18n("Type: %1
", mimeType->comment()); + + if (entry.contains(Owner)) { + text += i18n("Owner: %1
", entry[ Owner ].toString()); + } + + if (entry.contains(Group)) { + text += i18n("Group: %1
", entry[ Group ].toString()); + } + + if (entry.contains(Link)) { + text += i18n("Target: %1
", entry[ Link ].toString()); + } + + if (entry.contains(IsPasswordProtected) && entry[ IsPasswordProtected ].toBool()) { + text += i18n("Password protected: Yes
"); + } + + return text; +} + +#include "infopanel.moc" diff --git a/ark/part/infopanel.h b/ark/part/infopanel.h new file mode 100644 index 00000000..874d9b35 --- /dev/null +++ b/ark/part/infopanel.h @@ -0,0 +1,76 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2007 Henrique Pinto + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef INFOPANEL_H +#define INFOPANEL_H + +#include "kerfuffle/archive.h" +#include "archivemodel.h" +#include "ui_infopanel.h" + +#include + +class InfoPanel: public QFrame, Ui::InformationPanel +{ + Q_OBJECT +public: + explicit InfoPanel(ArchiveModel *model, QWidget *parent = 0); + virtual ~InfoPanel(); + + void setIndex(const QModelIndex &); + void setIndexes(const QModelIndexList &list); + + /** + * Returns the file name that is displayed on the info panel. + * + * @return The current file name. If no pretty name has been + * set, it returns the name of the loaded archive. + */ + QString prettyFileName() const; + + /** + * Sets a different file name for the current open archive. + * + * This is particularly useful when a temporary archive (from + * a remote location) is loaded, and the window title shows the + * remote file name and the info panel, by default, would show + * the name of the temporary downloaded file. + * + * @param fileName The new file name. + */ + void setPrettyFileName(const QString& fileName); + + void updateWithDefaults(); + +private: + void showMetaData(); + void hideMetaData(); + + void showActions(); + void hideActions(); + + QString metadataTextFor(const QModelIndex &); + + ArchiveModel *m_model; + QString m_prettyFileName; +}; + +#endif // INFOPANEL_H diff --git a/ark/part/infopanel.ui b/ark/part/infopanel.ui new file mode 100644 index 00000000..c6be12f1 --- /dev/null +++ b/ark/part/infopanel.ui @@ -0,0 +1,126 @@ + + + InformationPanel + + + + 0 + 0 + 118 + 300 + + + + + 0 + 0 + + + + Information Panel + + + + + + + + + Qt::AlignCenter + + + + + + + + 75 + true + + + + KSqueezedTextLabel + + + Qt::AlignCenter + + + true + + + Qt::ElideRight + + + + + + + Unknown file type + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + + + + Metadata Label + + + 10 + + + 20 + + + + + + + Qt::Horizontal + + + + + + + ActionsLabel + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + KSqueezedTextLabel + QLabel +
ksqueezedtextlabel.h
+
+
+ + +
diff --git a/ark/part/interface.h b/ark/part/interface.h new file mode 100644 index 00000000..40f59028 --- /dev/null +++ b/ark/part/interface.h @@ -0,0 +1,37 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2007 Henrique Pinto + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef INTERFACE_H +#define INTERFACE_H + +#include +#include + +class Interface +{ +public: + virtual ~Interface() {} + + virtual bool isBusy() const = 0; +}; + +Q_DECLARE_INTERFACE(Interface, "org.kde.kerfuffle.partinterface/0.42") + +#endif // INTERFACE_H diff --git a/ark/part/jobtracker.cpp b/ark/part/jobtracker.cpp new file mode 100644 index 00000000..8ed0ec32 --- /dev/null +++ b/ark/part/jobtracker.cpp @@ -0,0 +1,107 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2007 Henrique Pinto + * Copyright (C) 2008 Harald Hvaal + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "jobtracker.h" + +#include + +JobTrackerWidget::JobTrackerWidget(QWidget *parent) + : QFrame(parent) +{ + setupUi(this); +} + +JobTracker::JobTracker(QWidget *parent) + : KAbstractWidgetJobTracker(parent) +{ + m_ui = new JobTrackerWidget(parent); + resetUi(); +} + +JobTracker::~JobTracker() +{ + foreach(KJob *job, m_jobs) { + job->kill(); + delete job; + } +} + +void JobTracker::description(KJob *job, const QString &title, const QPair< QString, QString > &f1, const QPair< QString, QString > &f2) +{ + Q_UNUSED(job) + Q_UNUSED(f1) + Q_UNUSED(f2) + m_ui->descriptionLabel->setText(QString(QLatin1String( "%1" )).arg(title)); + m_ui->descriptionLabel->show(); +} + +void JobTracker::infoMessage(KJob *job, const QString &plain, const QString &rich) +{ + Q_UNUSED(job) + Q_UNUSED(rich) + m_ui->informationLabel->setText(plain); + m_ui->informationLabel->show(); +} + +void JobTracker::warning(KJob *job, const QString &plain, const QString &rich) +{ + Q_UNUSED(job) + Q_UNUSED(rich) + m_ui->informationLabel->setText(plain); +} + +void JobTracker::registerJob(KJob *job) +{ + m_jobs << job; + KJobTrackerInterface::registerJob(job); + m_ui->show(); + m_ui->informationLabel->hide(); + m_ui->progressBar->show(); +} + +void JobTracker::percent(KJob *job, unsigned long percent) +{ + Q_UNUSED(job) + m_ui->progressBar->setMaximum(100); + m_ui->progressBar->setMinimum(0); + m_ui->progressBar->setValue(percent); +} + +void JobTracker::unregisterJob(KJob *job) +{ + m_jobs.remove(job); + KJobTrackerInterface::unregisterJob(job); + resetUi(); +} + +void JobTracker::resetUi() +{ + m_ui->hide(); + m_ui->descriptionLabel->hide(); + m_ui->informationLabel->hide(); + m_ui->progressBar->setMaximum(0); + m_ui->progressBar->setMinimum(0); +} + +QWidget* JobTracker::widget(KJob *) +{ + return m_ui; +} diff --git a/ark/part/jobtracker.h b/ark/part/jobtracker.h new file mode 100644 index 00000000..08aa77a1 --- /dev/null +++ b/ark/part/jobtracker.h @@ -0,0 +1,68 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2007 Henrique Pinto + * Copyright (C) 2008 Harald Hvaal + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef JOBTRACKER_H +#define JOBTRACKER_H + +#include +#include +#include "ui_jobtracker.h" + +class KJob; + +class JobTrackerWidget: public QFrame, public Ui::JobTrackerWidget +{ + Q_OBJECT + +public: + JobTrackerWidget(QWidget *parent = 0); +}; + +class JobTracker: public KAbstractWidgetJobTracker +{ + Q_OBJECT + +public: + JobTracker(QWidget *parent = 0); + ~JobTracker(); + + virtual QWidget *widget(KJob *); + +public slots: + virtual void registerJob(KJob *job); + virtual void unregisterJob(KJob *job); + +protected slots: + virtual void description(KJob *job, const QString &title, const QPair< QString, QString > &f1, const QPair< QString, QString > &f2); + virtual void infoMessage(KJob *job, const QString &plain, const QString &rich); + virtual void warning(KJob *job, const QString &plain, const QString &rich); + + virtual void percent(KJob *job, unsigned long percent); + +private slots: + void resetUi(); + +private: + JobTrackerWidget *m_ui; + QSet m_jobs; +}; + +#endif // JOBTRACKER_H diff --git a/ark/part/jobtracker.ui b/ark/part/jobtracker.ui new file mode 100644 index 00000000..2b227793 --- /dev/null +++ b/ark/part/jobtracker.ui @@ -0,0 +1,92 @@ + + JobTrackerWidget + + + + 0 + 0 + 409 + 16 + + + + Job Tracker + + + + 4 + + + 1 + + + 4 + + + 1 + + + + + + 0 + 0 + + + + + 50 + 0 + + + + <b>Job Description</b> + + + + + + + + 0 + 0 + + + + + 50 + 0 + + + + Some Information about the job + + + + + + + + 0 + 0 + + + + + 50 + 0 + + + + 100 + + + -1 + + + + + + + + diff --git a/ark/part/part.cpp b/ark/part/part.cpp new file mode 100644 index 00000000..bae48825 --- /dev/null +++ b/ark/part/part.cpp @@ -0,0 +1,916 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2007 Henrique Pinto + * Copyright (C) 2008-2009 Harald Hvaal + * Copyright (C) 2009-2012 Raphael Kubo da Costa + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "part.h" +#include "archivemodel.h" +#include "archiveview.h" +#include "arkviewer.h" +#include "dnddbusinterfaceadaptor.h" +#include "infopanel.h" +#include "jobtracker.h" +#include "kerfuffle/archive.h" +#include "kerfuffle/extractiondialog.h" +#include "kerfuffle/jobs.h" +#include "kerfuffle/settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Kerfuffle; + +K_PLUGIN_FACTORY(Factory, registerPlugin();) +K_EXPORT_PLUGIN(Factory("ark")) + +namespace Ark +{ + +static quint32 s_instanceCounter = 1; + +Part::Part(QWidget *parentWidget, QObject *parent, const QVariantList& args) + : KParts::ReadWritePart(parent), + m_splitter(0), + m_busy(false), + m_jobTracker(0) +{ + Q_UNUSED(args) + setComponentData(Factory::componentData(), false); + + new DndExtractAdaptor(this); + + const QString pathName = QString(QLatin1String("/DndExtract/%1")).arg(s_instanceCounter++); + if (!QDBusConnection::sessionBus().registerObject(pathName, this)) { + kFatal() << "Could not register a D-Bus object for drag'n'drop"; + } + + m_model = new ArchiveModel(pathName, this); + + m_splitter = new QSplitter(Qt::Horizontal, parentWidget); + setWidget(m_splitter); + + m_view = new ArchiveView; + m_infoPanel = new InfoPanel(m_model); + + m_splitter->addWidget(m_view); + m_splitter->addWidget(m_infoPanel); + + QList splitterSizes = ArkSettings::splitterSizes(); + if (splitterSizes.isEmpty()) { + splitterSizes.append(200); + splitterSizes.append(100); + } + m_splitter->setSizes(splitterSizes); + + setupView(); + setupActions(); + + connect(m_model, SIGNAL(loadingStarted()), + this, SLOT(slotLoadingStarted())); + connect(m_model, SIGNAL(loadingFinished(KJob*)), + this, SLOT(slotLoadingFinished(KJob*))); + connect(m_model, SIGNAL(droppedFiles(QStringList,QString)), + this, SLOT(slotAddFiles(QStringList,QString))); + connect(m_model, SIGNAL(error(QString,QString)), + this, SLOT(slotError(QString,QString))); + + connect(this, SIGNAL(busy()), + this, SLOT(setBusyGui())); + connect(this, SIGNAL(ready()), + this, SLOT(setReadyGui())); + connect(this, SIGNAL(completed()), + this, SLOT(setFileNameFromArchive())); + + m_statusBarExtension = new KParts::StatusBarExtension(this); + + setXMLFile(QLatin1String( "ark_part.rc" )); +} + +Part::~Part() +{ + factory()->removeClient(this); + + saveSplitterSizes(); + + m_extractFilesAction->menu()->deleteLater(); +} + +void Part::registerJob(KJob* job) +{ + if (!m_jobTracker) { + m_jobTracker = new JobTracker(widget()); + m_statusBarExtension->addStatusBarItem(m_jobTracker->widget(0), 0, true); + m_jobTracker->widget(job)->show(); + } + m_jobTracker->registerJob(job); + + emit busy(); + connect(job, SIGNAL(result(KJob*)), this, SIGNAL(ready())); +} + +// TODO: One should construct a KUrl out of localPath in order to be able to handle +// non-local destinations (ie. trash:/ or a remote location) +// See bugs #189322 and #204323. +void Part::extractSelectedFilesTo(const QString& localPath) +{ + kDebug() << "Extract to " << localPath; + if (!m_model) { + return; + } + + if (m_view->selectionModel()->selectedRows().count() != 1) { + m_view->selectionModel()->setCurrentIndex(m_view->currentIndex(), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + } + if (m_view->selectionModel()->selectedRows().count() != 1) { + return; + } + + QVariant internalRoot; + kDebug() << "valid " << m_view->currentIndex().parent().isValid(); + if (m_view->currentIndex().parent().isValid()) { + internalRoot = m_model->entryForIndex(m_view->currentIndex().parent()).value(InternalID); + } + + if (internalRoot.isNull()) { + //we have the special case valid parent, but the parent does not + //actually correspond to an item in the archive, but an automatically + //created folder. for now, we will just use the filename of the node + //instead, but for plugins that rely on a non-filename value as the + //InternalId, this WILL break things. TODO find a solution + internalRoot = m_model->entryForIndex(m_view->currentIndex().parent()).value(FileName); + } + + QList files = selectedFilesWithChildren(); + if (files.isEmpty()) { + return; + } + + kDebug() << "selected files are " << files; + Kerfuffle::ExtractionOptions options; + options[QLatin1String( "PreservePaths" )] = true; + if (!internalRoot.isNull()) { + options[QLatin1String("RootNode")] = internalRoot; + } + + ExtractJob *job = m_model->extractFiles(files, localPath, options); + registerJob(job); + + connect(job, SIGNAL(result(KJob*)), + this, SLOT(slotExtractionDone(KJob*))); + + job->start(); +} + +void Part::setupView() +{ + m_view->setModel(m_model); + + m_view->setSortingEnabled(true); + + connect(m_view->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(updateActions())); + connect(m_view->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(selectionChanged())); + + //TODO: fix an actual eventhandler + connect(m_view, SIGNAL(itemTriggered(QModelIndex)), + this, SLOT(slotPreview(QModelIndex))); + + connect(m_model, SIGNAL(columnsInserted(QModelIndex,int,int)), + this, SLOT(adjustColumns())); +} + +void Part::setupActions() +{ + KToggleAction *showInfoPanelAction = new KToggleAction(i18nc("@action:inmenu", "Show information panel"), this); + actionCollection()->addAction(QLatin1String( "show-infopanel" ), showInfoPanelAction); + showInfoPanelAction->setChecked(m_splitter->sizes().at(1) > 0); + connect(showInfoPanelAction, SIGNAL(triggered(bool)), + this, SLOT(slotToggleInfoPanel(bool))); + + m_saveAsAction = KStandardAction::saveAs(this, SLOT(slotSaveAs()), actionCollection()); + + m_previewAction = actionCollection()->addAction(QLatin1String( "preview" )); + m_previewAction->setText(i18nc("to preview a file inside an archive", "Pre&view")); + m_previewAction->setIcon(KIcon( QLatin1String( "document-preview-archive" ))); + m_previewAction->setStatusTip(i18n("Click to preview the selected file")); + m_previewAction->setShortcuts(QList() << Qt::Key_Return << Qt::Key_Space); + connect(m_previewAction, SIGNAL(triggered(bool)), + this, SLOT(slotPreview())); + + m_extractFilesAction = actionCollection()->addAction(QLatin1String( "extract" )); + m_extractFilesAction->setText(i18n("E&xtract")); + m_extractFilesAction->setIcon(KIcon( QLatin1String( "archive-extract" ))); + m_extractFilesAction->setStatusTip(i18n("Click to open an extraction dialog, where you can choose to extract either all files or just the selected ones")); + m_extractFilesAction->setShortcut(QKeySequence( QLatin1String( "Ctrl+E" ) )); + connect(m_extractFilesAction, SIGNAL(triggered(bool)), + this, SLOT(slotExtractFiles())); + + m_addFilesAction = actionCollection()->addAction(QLatin1String( "add" )); + m_addFilesAction->setIcon(KIcon( QLatin1String( "archive-insert" ))); + m_addFilesAction->setText(i18n("Add &File...")); + m_addFilesAction->setStatusTip(i18n("Click to add files to the archive")); + connect(m_addFilesAction, SIGNAL(triggered(bool)), + this, SLOT(slotAddFiles())); + + m_addDirAction = actionCollection()->addAction(QLatin1String( "add-dir" )); + m_addDirAction->setIcon(KIcon( QLatin1String( "archive-insert-directory" ))); + m_addDirAction->setText(i18n("Add Fo&lder...")); + m_addDirAction->setStatusTip(i18n("Click to add a folder to the archive")); + connect(m_addDirAction, SIGNAL(triggered(bool)), + this, SLOT(slotAddDir())); + + m_deleteFilesAction = actionCollection()->addAction(QLatin1String( "delete" )); + m_deleteFilesAction->setIcon(KIcon( QLatin1String( "archive-remove" ))); + m_deleteFilesAction->setText(i18n("De&lete")); + m_deleteFilesAction->setShortcut(Qt::Key_Delete); + m_deleteFilesAction->setStatusTip(i18n("Click to delete the selected files")); + connect(m_deleteFilesAction, SIGNAL(triggered(bool)), + this, SLOT(slotDeleteFiles())); + + updateActions(); +} + +void Part::updateActions() +{ + bool isWritable = m_model->archive() && (!m_model->archive()->isReadOnly()); + + m_previewAction->setEnabled(!isBusy() && (m_view->selectionModel()->selectedRows().count() == 1) + && isPreviewable(m_view->selectionModel()->currentIndex())); + m_extractFilesAction->setEnabled(!isBusy() && (m_model->rowCount() > 0)); + m_addFilesAction->setEnabled(!isBusy() && isWritable); + m_addDirAction->setEnabled(!isBusy() && isWritable); + m_deleteFilesAction->setEnabled(!isBusy() && (m_view->selectionModel()->selectedRows().count() > 0) + && isWritable); + + QMenu *menu = m_extractFilesAction->menu(); + if (!menu) { + menu = new QMenu; + m_extractFilesAction->setMenu(menu); + connect(menu, SIGNAL(triggered(QAction*)), + this, SLOT(slotQuickExtractFiles(QAction*))); + + // Remember to keep this action's properties as similar to + // m_extractFilesAction's as possible (except where it does not make + // sense, such as the text or the shortcut). + QAction *extractTo = menu->addAction(i18n("Extract To...")); + extractTo->setIcon(m_extractFilesAction->icon()); + extractTo->setStatusTip(m_extractFilesAction->statusTip()); + connect(extractTo, SIGNAL(triggered(bool)), SLOT(slotExtractFiles())); + + menu->addSeparator(); + + QAction *header = menu->addAction(i18n("Quick Extract To...")); + header->setEnabled(false); + header->setIcon(KIcon( QLatin1String( "archive-extract" ))); + } + + while (menu->actions().size() > 3) { + menu->removeAction(menu->actions().last()); + } + + const KConfigGroup conf(KGlobal::config(), "DirSelect Dialog"); + const QStringList dirHistory = conf.readPathEntry("History Items", QStringList()); + + for (int i = 0; i < qMin(10, dirHistory.size()); ++i) { + const KUrl dirUrl(dirHistory.at(i)); + QAction *newAction = menu->addAction(dirUrl.pathOrUrl()); + newAction->setData(dirUrl.pathOrUrl()); + } +} + +void Part::slotQuickExtractFiles(QAction *triggeredAction) +{ + // #190507: triggeredAction->data.isNull() means it's the "Extract to..." + // action, and we do not want it to run here + if (!triggeredAction->data().isNull()) { + kDebug() << "Extract to " << triggeredAction->data().toString(); + + const QString userDestination = triggeredAction->data().toString(); + QString finalDestinationDirectory; + const QString detectedSubfolder = detectSubfolder(); + + if (!isSingleFolderArchive()) { + finalDestinationDirectory = userDestination + + QDir::separator() + detectedSubfolder; + QDir(userDestination).mkdir(detectedSubfolder); + } else { + finalDestinationDirectory = userDestination; + } + + Kerfuffle::ExtractionOptions options; + options[QLatin1String( "PreservePaths" )] = true; + QList files = selectedFiles(); + ExtractJob *job = m_model->extractFiles(files, finalDestinationDirectory, options); + registerJob(job); + + connect(job, SIGNAL(result(KJob*)), + this, SLOT(slotExtractionDone(KJob*))); + + job->start(); + } +} + +bool Part::isPreviewable(const QModelIndex& index) const +{ + return index.isValid() && (!m_model->entryForIndex(index)[ IsDirectory ].toBool()); +} + +void Part::selectionChanged() +{ + m_infoPanel->setIndexes(m_view->selectionModel()->selectedRows()); +} + +KAboutData* Part::createAboutData() +{ + return new KAboutData("ark", 0, ki18n("ArkPart"), "3.0"); +} + +bool Part::openFile() +{ + const QString localFile(localFilePath()); + const QFileInfo localFileInfo(localFile); + const bool creatingNewArchive = + arguments().metaData()[QLatin1String("createNewArchive")] == QLatin1String("true"); + + if (localFileInfo.isDir()) { + KMessageBox::error(NULL, i18nc("@info", + "%1 is a directory.", + localFile)); + return false; + } + + if (creatingNewArchive) { + if (localFileInfo.exists()) { + int overwrite = KMessageBox::questionYesNo(NULL, i18nc("@info", "The archive %1 already exists. Would you like to open it instead?", localFile), i18nc("@title:window", "File Exists"), KGuiItem(i18n("Open File")), KStandardGuiItem::cancel()); + + if (overwrite == KMessageBox::No) { + return false; + } + } + } else { + if (!localFileInfo.exists()) { + KMessageBox::sorry(NULL, i18nc("@info", "The archive %1 was not found.", localFile), i18nc("@title:window", "Error Opening Archive")); + return false; + } + } + + QScopedPointer archive(Kerfuffle::Archive::create(localFile, m_model)); + + if ((!archive) || ((creatingNewArchive) && (archive->isReadOnly()))) { + QStringList mimeTypeList; + QHash mimeTypes; + + if (creatingNewArchive) { + mimeTypeList = Kerfuffle::supportedWriteMimeTypes(); + } else { + mimeTypeList = Kerfuffle::supportedMimeTypes(); + } + + foreach(const QString& mime, mimeTypeList) { + KMimeType::Ptr mimePtr(KMimeType::mimeType(mime)); + if (mimePtr) { + // Key = "application/zip", Value = "Zip Archive" + mimeTypes[mime] = mimePtr->comment(); + } + } + + QStringList mimeComments(mimeTypes.values()); + mimeComments.sort(); + + bool ok; + QString item; + + if (creatingNewArchive) { + item = KInputDialog::getItem(i18nc("@title:window", "Invalid Archive Type"), + i18nc("@info", "Ark cannot create archives of the type you have chosen.Please choose another archive type below."), + mimeComments, 0, false, &ok); + } else { + item = KInputDialog::getItem(i18nc("@title:window", "Unable to Determine Archive Type"), + i18nc("@info", "Ark was unable to determine the archive type of the filename.Please choose the correct archive type below."), + mimeComments, + 0, + false, + &ok); + } + + if ((!ok) || (item.isEmpty())) { + return false; + } + + archive.reset(Kerfuffle::Archive::create(localFile, mimeTypes.key(item), m_model)); + } + + if (!archive) { + KMessageBox::sorry(NULL, i18nc("@info", "Ark was not able to open the archive %1. No plugin capable of handling the file was found.", localFile), i18nc("@title:window", "Error Opening Archive")); + return false; + } + + KJob *job = m_model->setArchive(archive.take()); + registerJob(job); + job->start(); + m_infoPanel->setIndex(QModelIndex()); + + if (arguments().metaData()[QLatin1String( "showExtractDialog" )] == QLatin1String( "true" )) { + QTimer::singleShot(0, this, SLOT(slotExtractFiles())); + } + + return true; +} + +bool Part::saveFile() +{ + return true; +} + +bool Part::isBusy() const +{ + return m_busy; +} + +void Part::slotLoadingStarted() +{ +} + +void Part::slotLoadingFinished(KJob *job) +{ + kDebug(); + + if (job->error()) { + if (arguments().metaData()[QLatin1String( "createNewArchive" )] != QLatin1String( "true" )) { + KMessageBox::sorry(NULL, i18nc("@info", "Loading the archive %1 failed with the following error: %2", localFilePath(), job->errorText()), i18nc("@title:window", "Error Opening Archive")); + + // The file failed to open, so reset the open archive, info panel and caption. + m_model->setArchive(NULL); + + m_infoPanel->setPrettyFileName(QString()); + m_infoPanel->updateWithDefaults(); + + emit setWindowCaption(QString()); + } + } + + m_view->sortByColumn(0, Qt::AscendingOrder); + m_view->expandToDepth(0); + + // After loading all files, resize the columns to fit all fields + m_view->header()->resizeSections(QHeaderView::ResizeToContents); + + updateActions(); +} + +void Part::setReadyGui() +{ + kDebug(); + QApplication::restoreOverrideCursor(); + m_busy = false; + m_view->setEnabled(true); + updateActions(); +} + +void Part::setBusyGui() +{ + kDebug(); + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + m_busy = true; + m_view->setEnabled(false); + updateActions(); +} + +void Part::setFileNameFromArchive() +{ + const QString prettyName = url().fileName(); + + m_infoPanel->setPrettyFileName(prettyName); + m_infoPanel->updateWithDefaults(); + + emit setWindowCaption(prettyName); +} + +void Part::slotPreview() +{ + slotPreview(m_view->selectionModel()->currentIndex()); +} + +void Part::slotPreview(const QModelIndex & index) +{ + if (!isPreviewable(index)) { + return; + } + + const ArchiveEntry& entry = m_model->entryForIndex(index); + + if (!entry.isEmpty()) { + Kerfuffle::ExtractionOptions options; + options[QLatin1String( "PreservePaths" )] = true; + + ExtractJob *job = m_model->extractFile(entry[ InternalID ], m_previewDir.name(), options); + registerJob(job); + connect(job, SIGNAL(result(KJob*)), + this, SLOT(slotPreviewExtracted(KJob*))); + job->start(); + } +} + +void Part::slotPreviewExtracted(KJob *job) +{ + // FIXME: the error checking here isn't really working + // if there's an error or an overwrite dialog, + // the preview dialog will be launched anyway + if (!job->error()) { + const ArchiveEntry& entry = + m_model->entryForIndex(m_view->selectionModel()->currentIndex()); + + QString fullName = + m_previewDir.name() + QLatin1Char('/') + entry[FileName].toString(); + + // Make sure a maliciously crafted archive with parent folders named ".." do + // not cause the previewed file path to be located outside the temporary + // directory, resulting in a directory traversal issue. + fullName.remove(QLatin1String("../")); + + ArkViewer::view(fullName, widget()); + } else { + KMessageBox::error(widget(), job->errorString()); + } + setReadyGui(); +} + +void Part::slotError(const QString& errorMessage, const QString& details) +{ + if (details.isEmpty()) { + KMessageBox::error(widget(), errorMessage); + } else { + KMessageBox::detailedError(widget(), errorMessage, details); + } +} + +bool Part::isSingleFolderArchive() const +{ + return m_model->archive()->isSingleFolderArchive(); +} + +QString Part::detectSubfolder() const +{ + if (!m_model) { + return QString(); + } + + return m_model->archive()->subfolderName(); +} + +void Part::slotExtractFiles() +{ + if (!m_model) { + return; + } + + QWeakPointer dialog = new Kerfuffle::ExtractionDialog; + + if (m_view->selectionModel()->selectedRows().count() > 0) { + dialog.data()->setShowSelectedFiles(true); + } + + dialog.data()->setSingleFolderArchive(isSingleFolderArchive()); + dialog.data()->setSubfolder(detectSubfolder()); + + dialog.data()->setCurrentUrl(QFileInfo(m_model->archive()->fileName()).path()); + + if (dialog.data()->exec()) { + //this is done to update the quick extract menu + updateActions(); + + QVariantList files; + + //if the user has chosen to extract only selected entries, fetch these + //from the listview + if (!dialog.data()->extractAllFiles()) { + files = selectedFilesWithChildren(); + } + + kDebug() << "Selected " << files; + + Kerfuffle::ExtractionOptions options; + + if (dialog.data()->preservePaths()) { + options[QLatin1String("PreservePaths")] = true; + } + + options[QLatin1String("FollowExtractionDialogSettings")] = true; + + const QString destinationDirectory = dialog.data()->destinationDirectory().pathOrUrl(); + ExtractJob *job = m_model->extractFiles(files, destinationDirectory, options); + registerJob(job); + + connect(job, SIGNAL(result(KJob*)), + this, SLOT(slotExtractionDone(KJob*))); + + job->start(); + } + + delete dialog.data(); +} + +QList Part::selectedFilesWithChildren() +{ + Q_ASSERT(m_model); + + QModelIndexList toIterate = m_view->selectionModel()->selectedRows(); + + for (int i = 0; i < toIterate.size(); ++i) { + QModelIndex index = toIterate.at(i); + + for (int j = 0; j < m_model->rowCount(index); ++j) { + QModelIndex child = m_model->index(j, 0, index); + if (!toIterate.contains(child)) { + toIterate << child; + } + } + } + + QVariantList ret; + foreach(const QModelIndex & index, toIterate) { + const ArchiveEntry& entry = m_model->entryForIndex(index); + if (entry.contains(InternalID)) { + ret << entry[ InternalID ]; + } + } + return ret; +} + +QList Part::selectedFiles() +{ + QStringList toSort; + + foreach(const QModelIndex & index, m_view->selectionModel()->selectedRows()) { + const ArchiveEntry& entry = m_model->entryForIndex(index); + toSort << entry[ InternalID ].toString(); + } + + toSort.sort(); + QVariantList ret; + foreach(const QString &i, toSort) { + ret << i; + } + return ret; +} + +void Part::slotExtractionDone(KJob* job) +{ + kDebug(); + if (job->error()) { + KMessageBox::error(widget(), job->errorString()); + } else { + ExtractJob *extractJob = qobject_cast(job); + Q_ASSERT(extractJob); + + const bool followExtractionDialogSettings = + extractJob->extractionOptions().value(QLatin1String("FollowExtractionDialogSettings"), false).toBool(); + if (!followExtractionDialogSettings) { + return; + } + + if (ArkSettings::openDestinationFolderAfterExtraction()) { + + KUrl destinationDirectory(extractJob->destinationDirectory()); + destinationDirectory.cleanPath(); + + KRun::runUrl(destinationDirectory, QLatin1String("inode/directory"), widget()); + } + + if (ArkSettings::closeAfterExtraction()) { + emit quit(); + } + } +} + +void Part::adjustColumns() +{ + kDebug(); + + m_view->header()->setResizeMode(0, QHeaderView::ResizeToContents); +} + +void Part::slotAddFiles(const QStringList& filesToAdd, const QString& path) +{ + if (filesToAdd.isEmpty()) { + return; + } + + kDebug() << "Adding " << filesToAdd << " to " << path; + kDebug() << "Warning, for now the path argument is not implemented"; + + QStringList cleanFilesToAdd(filesToAdd); + for (int i = 0; i < cleanFilesToAdd.size(); ++i) { + QString& file = cleanFilesToAdd[i]; + if (QFileInfo(file).isDir()) { + if (!file.endsWith(QLatin1Char( '/' ))) { + file += QLatin1Char( '/' ); + } + } + } + + CompressionOptions options; + + QString firstPath = cleanFilesToAdd.first(); + if (firstPath.right(1) == QLatin1String( "/" )) { + firstPath.chop(1); + } + firstPath = QFileInfo(firstPath).dir().absolutePath(); + + kDebug() << "Detected relative path to be " << firstPath; + options[QLatin1String( "GlobalWorkDir" )] = firstPath; + + AddJob *job = m_model->addFiles(cleanFilesToAdd, options); + if (!job) { + return; + } + + connect(job, SIGNAL(result(KJob*)), + this, SLOT(slotAddFilesDone(KJob*))); + registerJob(job); + job->start(); +} + +void Part::slotAddFiles() +{ + kDebug(); + + // #264819: passing widget() as the parent will not work as expected. + // KFileDialog will create a KFileWidget, which runs an internal + // event loop to stat the given directory. This, in turn, leads to + // events being delivered to widget(), which is a QSplitter, which + // in turn reimplements childEvent() and will end up calling + // QWidget::show() on the KFileDialog (thus showing it in a + // non-modal state). + // When KFileDialog::exec() is called, the widget is already shown + // and nothing happens. + const QStringList filesToAdd = + KFileDialog::getOpenFileNames(KUrl("kfiledialog:///ArkAddFiles"), + QString(), widget()->parentWidget(), + i18nc("@title:window", "Add Files")); + + slotAddFiles(filesToAdd); +} + +void Part::slotAddDir() +{ + kDebug(); + const QString dirToAdd = KFileDialog::getExistingDirectory(KUrl("kfiledialog:///ArkAddFiles"), widget(), i18nc("@title:window", "Add Folder")); + + if (!dirToAdd.isEmpty()) { + slotAddFiles(QStringList() << dirToAdd); + } +} + +void Part::slotAddFilesDone(KJob* job) +{ + kDebug(); + if (job->error()) { + KMessageBox::error(widget(), job->errorString()); + } +} + +void Part::slotDeleteFilesDone(KJob* job) +{ + kDebug(); + if (job->error()) { + KMessageBox::error(widget(), job->errorString()); + } +} + +void Part::slotDeleteFiles() +{ + kDebug(); + + const int reallyDelete = + KMessageBox::questionYesNo(NULL, + i18n("Deleting these files is not undoable. Are you sure you want to do this?"), + i18nc("@title:window", "Delete files"), + KStandardGuiItem::del(), + KStandardGuiItem::cancel(), + QString(), + KMessageBox::Dangerous | KMessageBox::Notify); + + if (reallyDelete == KMessageBox::No) { + return; + } + + DeleteJob *job = m_model->deleteFiles(selectedFilesWithChildren()); + connect(job, SIGNAL(result(KJob*)), + this, SLOT(slotDeleteFilesDone(KJob*))); + registerJob(job); + job->start(); +} + +void Part::slotToggleInfoPanel(bool visible) +{ + QList splitterSizes; + + if (visible) { + splitterSizes = ArkSettings::splitterSizesWithBothWidgets(); + } else { + splitterSizes = m_splitter->sizes(); + ArkSettings::setSplitterSizesWithBothWidgets(splitterSizes); + splitterSizes[1] = 0; + } + + m_splitter->setSizes(splitterSizes); + saveSplitterSizes(); +} + +void Part::saveSplitterSizes() +{ + ArkSettings::setSplitterSizes(m_splitter->sizes()); + ArkSettings::self()->writeConfig(); +} + +void Part::slotSaveAs() +{ + KUrl saveUrl = KFileDialog::getSaveUrl(KUrl(QLatin1String( "kfiledialog:///ArkSaveAs/" ) + url().fileName()), QString(), widget()); + + if ((saveUrl.isValid()) && (!saveUrl.isEmpty())) { + if (KIO::NetAccess::exists(saveUrl, KIO::NetAccess::DestinationSide, widget())) { + int overwrite = KMessageBox::warningContinueCancel(widget(), + i18nc("@info", "An archive named %1 already exists. Are you sure you want to overwrite it?", saveUrl.fileName()), + QString(), + KStandardGuiItem::overwrite()); + + if (overwrite != KMessageBox::Continue) { + return; + } + } + + KUrl srcUrl = KUrl::fromPath(localFilePath()); + + if (!QFile::exists(localFilePath())) { + if (url().isLocalFile()) { + KMessageBox::error(widget(), + i18nc("@info", "The archive %1 cannot be copied to the specified location. The archive does not exist anymore.", localFilePath())); + + return; + } else { + srcUrl = url(); + } + } + + KIO::Job *copyJob = KIO::file_copy(srcUrl, saveUrl, -1, KIO::Overwrite); + + if (!KIO::NetAccess::synchronousRun(copyJob, widget())) { + KMessageBox::error(widget(), + i18nc("@info", "The archive could not be saved as %1. Try saving it to another location.", saveUrl.pathOrUrl())); + } + } +} + +} // namespace Ark diff --git a/ark/part/part.h b/ark/part/part.h new file mode 100644 index 00000000..5379b9fc --- /dev/null +++ b/ark/part/part.h @@ -0,0 +1,126 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2007 Henrique Pinto + * Copyright (C) 2008-2009 Harald Hvaal + * Copyright (C) 2009 Raphael Kubo da Costa + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef PART_H +#define PART_H + +#include "interface.h" + +#include +#include +#include + +#include + +class ArchiveModel; +class InfoPanel; + +class KAbstractWidgetJobTracker; +class KAboutData; +class KAction; +class KJob; + +class QAction; +class QSplitter; +class QTreeView; + +namespace Ark +{ + +class Part: public KParts::ReadWritePart, public Interface +{ + Q_OBJECT + Q_INTERFACES(Interface) +public: + Part(QWidget *parentWidget, QObject *parent, const QVariantList &); + ~Part(); + static KAboutData* createAboutData(); + + virtual bool openFile(); + virtual bool saveFile(); + + bool isBusy() const; + +public slots: + void extractSelectedFilesTo(const QString& localPath); + +private slots: + void slotLoadingStarted(); + void slotLoadingFinished(KJob *job); + void slotPreview(); + void slotPreview(const QModelIndex & index); + void slotPreviewExtracted(KJob*); + void slotError(const QString& errorMessage, const QString& details); + void slotExtractFiles(); + void slotExtractionDone(KJob*); + void slotQuickExtractFiles(QAction*); + void slotAddFiles(); + void slotAddFiles(const QStringList& files, const QString& path = QString()); + void slotAddDir(); + void slotAddFilesDone(KJob*); + void slotDeleteFiles(); + void slotDeleteFilesDone(KJob*); + void saveSplitterSizes(); + void slotToggleInfoPanel(bool); + void slotSaveAs(); + void updateActions(); + void selectionChanged(); + void adjustColumns(); + void setBusyGui(); + void setReadyGui(); + void setFileNameFromArchive(); + +signals: + void busy(); + void ready(); + void quit(); + +private: + void setupView(); + void setupActions(); + bool isSingleFolderArchive() const; + QString detectSubfolder() const; + bool isPreviewable(const QModelIndex& index) const; + QList selectedFiles(); + QList selectedFilesWithChildren(); + void registerJob(KJob *job); + + ArchiveModel *m_model; + QTreeView *m_view; + KAction *m_previewAction; + KAction *m_extractFilesAction; + KAction *m_addFilesAction; + KAction *m_addDirAction; + KAction *m_deleteFilesAction; + KAction *m_saveAsAction; + InfoPanel *m_infoPanel; + QSplitter *m_splitter; + KTempDir m_previewDir; + bool m_busy; + + KAbstractWidgetJobTracker *m_jobTracker; + KParts::StatusBarExtension *m_statusBarExtension; +}; + +} // namespace Ark + +#endif // PART_H diff --git a/ark/plugins/CLI-README b/ark/plugins/CLI-README new file mode 100644 index 00000000..0c21d49e --- /dev/null +++ b/ark/plugins/CLI-README @@ -0,0 +1,21 @@ +In this folder is a general template of what one needs to implement +support for a plugin using the cli interface in ark. + +Here are the steps. + +1. First, create a copy of the cliplugin folder +2. Change plugins/CMakeLists.txt to include the new subfolder +3. Rename the kerfuffle_cli.desktop to a unique name, for example +kerfuffle_rar.desktop. +4. Fill in the parts in the desktop marked with TODO +5. Update the plugins/yourplugin/CMakeLists.txt file, replacing all +instances of kerfuffle_cli with kerfuffle_yourplugin (where yourplugin + must be a unique plugin name) +6. Implement/modify cliplugin.cpp to fit your archive type. +Refer to kerfuffle/cliinterface.h for explanations on the values that +needs to be implemented. The class name does not need to be changed + +Then finally, email the plugin to the ark maintainer for a code review before +it is committed to trunk :D + +Have fun diff --git a/ark/plugins/CMakeLists.txt b/ark/plugins/CMakeLists.txt new file mode 100644 index 00000000..36194128 --- /dev/null +++ b/ark/plugins/CMakeLists.txt @@ -0,0 +1,28 @@ +if (LIBARCHIVE_FOUND) + if( HAVE_LIBARCHIVE_READ_DISK_API ) + if( NOT HAVE_LIBARCHIVE_LZMA_SUPPORT OR NOT HAVE_LIBARCHIVE_XZ_SUPPORT ) + message(STATUS "Your libarchive does not have support for lzma and/or xz archives. libarchive >= 2.7.0 is recommended.") + endif( NOT HAVE_LIBARCHIVE_LZMA_SUPPORT OR NOT HAVE_LIBARCHIVE_XZ_SUPPORT ) + if( NOT HAVE_LIBARCHIVE_RPM_SUPPORT ) + message(STATUS "Your libarchive does not have support for rpm archives. libarchive >= 2.8.0 is required for this.") + endif( NOT HAVE_LIBARCHIVE_RPM_SUPPORT ) + if( NOT HAVE_LIBARCHIVE_CAB_SUPPORT ) + message(STATUS "Your libarchive does not have support for cab archives. libarchive >= 3.0.0 is required for this.") + endif( NOT HAVE_LIBARCHIVE_CAB_SUPPORT ) + add_subdirectory( libarchive ) + else( HAVE_LIBARCHIVE_READ_DISK_API ) + # Remove the cached variables from FindLibArchive.cmake + unset( LIBARCHIVE_FOUND ) + unset( LIBARCHIVE_INCLUDE_DIR ) + unset( LIBARCHIVE_LIBRARY ) + message(STATUS "Your libarchive does not have support for archive_read_disk api. libarchive >= 2.6.0 is needed.") + endif( HAVE_LIBARCHIVE_READ_DISK_API ) +endif (LIBARCHIVE_FOUND) + +add_subdirectory( clirarplugin ) +add_subdirectory( cli7zplugin ) +add_subdirectory( clizipplugin ) +add_subdirectory( libsinglefileplugin ) +add_subdirectory( clilhaplugin ) + +set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}" PARENT_SCOPE) diff --git a/ark/plugins/cli7zplugin/CMakeLists.txt b/ark/plugins/cli7zplugin/CMakeLists.txt new file mode 100644 index 00000000..da56df20 --- /dev/null +++ b/ark/plugins/cli7zplugin/CMakeLists.txt @@ -0,0 +1,21 @@ +########### next target ############### + +set(SUPPORTED_CLI7Z_MIMETYPES "application/x-7z-compressed;") + +set(kerfuffle_cli7z_SRCS cliplugin.cpp) + +kde4_add_plugin(kerfuffle_cli7z ${kerfuffle_cli7z_SRCS}) + +target_link_libraries(kerfuffle_cli7z ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS} kerfuffle ) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_cli7z.desktop.cmake + ${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_cli7z.desktop +) + +########### install files ############### + +install(TARGETS kerfuffle_cli7z DESTINATION ${PLUGIN_INSTALL_DIR} ) +install( FILES ${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_cli7z.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + +set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}${SUPPORTED_CLI7Z_MIMETYPES}" PARENT_SCOPE) diff --git a/ark/plugins/cli7zplugin/cliplugin.cpp b/ark/plugins/cli7zplugin/cliplugin.cpp new file mode 100644 index 00000000..500f121d --- /dev/null +++ b/ark/plugins/cli7zplugin/cliplugin.cpp @@ -0,0 +1,180 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2009 Harald Hvaal + * Copyright (C) 2009-2011 Raphael Kubo da Costa + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "cliplugin.h" +#include "kerfuffle/cliinterface.h" +#include "kerfuffle/kerfuffle_export.h" + +#include +#include +#include +#include + +#include + +using namespace Kerfuffle; + +CliPlugin::CliPlugin(QObject *parent, const QVariantList & args) + : CliInterface(parent, args) + , m_archiveType(ArchiveType7z) + , m_state(ReadStateHeader) +{ +} + +CliPlugin::~CliPlugin() +{ +} + +ParameterList CliPlugin::parameterList() const +{ + static ParameterList p; + + if (p.isEmpty()) { + //p[CaptureProgress] = true; + p[ListProgram] = p[ExtractProgram] = p[DeleteProgram] = p[AddProgram] = QStringList() << QLatin1String( "7z" ) << QLatin1String( "7za" ) << QLatin1String( "7zr" ); + + p[ListArgs] = QStringList() << QLatin1String( "l" ) << QLatin1String( "-slt" ) << QLatin1String( "$Archive" ); + p[ExtractArgs] = QStringList() << QLatin1String( "$PreservePathSwitch" ) << QLatin1String( "$PasswordSwitch" ) << QLatin1String( "$Archive" ) << QLatin1String( "$Files" ); + p[PreservePathSwitch] = QStringList() << QLatin1String( "x" ) << QLatin1String( "e" ); + p[PasswordSwitch] = QStringList() << QLatin1String( "-p$Password" ); + p[FileExistsExpression] = QLatin1String( "already exists. Overwrite with" ); + p[WrongPasswordPatterns] = QStringList() << QLatin1String( "Wrong password" ); + p[AddArgs] = QStringList() << QLatin1String( "a" ) << QLatin1String( "$Archive" ) << QLatin1String( "$Files" ); + p[DeleteArgs] = QStringList() << QLatin1String( "d" ) << QLatin1String( "$Archive" ) << QLatin1String( "$Files" ); + + p[FileExistsInput] = QStringList() + << QLatin1String( "Y" ) //overwrite + << QLatin1String( "N" )//skip + << QLatin1String( "A" ) //overwrite all + << QLatin1String( "S" ) //autoskip + << QLatin1String( "Q" ) //cancel + ; + + p[PasswordPromptPattern] = QLatin1String("Enter password \\(will not be echoed\\) :"); + } + + return p; +} + +bool CliPlugin::readListLine(const QString& line) +{ + static const QLatin1String archiveInfoDelimiter1("--"); // 7z 9.13+ + static const QLatin1String archiveInfoDelimiter2("----"); // 7z 9.04 + static const QLatin1String entryInfoDelimiter("----------"); + + switch (m_state) { + case ReadStateHeader: + if (line.startsWith(QLatin1String("Listing archive:"))) { + kDebug() << "Archive name: " + << line.right(line.size() - 16).trimmed(); + } else if ((line == archiveInfoDelimiter1) || + (line == archiveInfoDelimiter2)) { + m_state = ReadStateArchiveInformation; + } else if (line.contains(QLatin1String( "Error:" ))) { + kDebug() << line.mid(6); + } + break; + + case ReadStateArchiveInformation: + if (line == entryInfoDelimiter) { + m_state = ReadStateEntryInformation; + } else if (line.startsWith(QLatin1String("Type ="))) { + const QString type = line.mid(7).trimmed(); + kDebug() << "Archive type: " << type; + + if (type == QLatin1String("7z")) { + m_archiveType = ArchiveType7z; + } else if (type == QLatin1String("BZip2")) { + m_archiveType = ArchiveTypeBZip2; + } else if (type == QLatin1String("GZip")) { + m_archiveType = ArchiveTypeGZip; + } else if (type == QLatin1String("Tar")) { + m_archiveType = ArchiveTypeTar; + } else if (type == QLatin1String("Zip")) { + m_archiveType = ArchiveTypeZip; + } else { + // Should not happen + kWarning() << "Unsupported archive type"; + return false; + } + } + + break; + + case ReadStateEntryInformation: + if (line.startsWith(QLatin1String("Path ="))) { + const QString entryFilename = + QDir::fromNativeSeparators(line.mid(6).trimmed()); + m_currentArchiveEntry.clear(); + m_currentArchiveEntry[FileName] = entryFilename; + m_currentArchiveEntry[InternalID] = entryFilename; + } else if (line.startsWith(QLatin1String("Size = "))) { + m_currentArchiveEntry[ Size ] = line.mid(7).trimmed(); + } else if (line.startsWith(QLatin1String("Packed Size = "))) { + // #236696: 7z files only show a single Packed Size value + // corresponding to the whole archive. + if (m_archiveType != ArchiveType7z) { + m_currentArchiveEntry[CompressedSize] = line.mid(14).trimmed(); + } + } else if (line.startsWith(QLatin1String("Modified = "))) { + m_currentArchiveEntry[ Timestamp ] = + QDateTime::fromString(line.mid(11).trimmed(), + QLatin1String( "yyyy-MM-dd hh:mm:ss" )); + } else if (line.startsWith(QLatin1String("Attributes = "))) { + const QString attributes = line.mid(13).trimmed(); + + const bool isDirectory = attributes.startsWith(QLatin1Char( 'D' )); + m_currentArchiveEntry[ IsDirectory ] = isDirectory; + if (isDirectory) { + const QString directoryName = + m_currentArchiveEntry[FileName].toString(); + if (!directoryName.endsWith(QLatin1Char( '/' ))) { + const bool isPasswordProtected = (line.at(12) == QLatin1Char( '+' )); + m_currentArchiveEntry[FileName] = + m_currentArchiveEntry[InternalID] = QString(directoryName + QLatin1Char( '/' )); + m_currentArchiveEntry[ IsPasswordProtected ] = + isPasswordProtected; + } + } + + m_currentArchiveEntry[ Permissions ] = attributes.mid(1); + } else if (line.startsWith(QLatin1String("CRC = "))) { + m_currentArchiveEntry[ CRC ] = line.mid(6).trimmed(); + } else if (line.startsWith(QLatin1String("Method = "))) { + m_currentArchiveEntry[ Method ] = line.mid(9).trimmed(); + } else if (line.startsWith(QLatin1String("Encrypted = ")) && + line.size() >= 13) { + m_currentArchiveEntry[ IsPasswordProtected ] = (line.at(12) == QLatin1Char( '+' )); + } else if (line.startsWith(QLatin1String("Block = "))) { + if (m_currentArchiveEntry.contains(FileName)) { + emit entry(m_currentArchiveEntry); + } + } + break; + } + + return true; +} + +KERFUFFLE_EXPORT_PLUGIN(CliPlugin) + +#include "cliplugin.moc" diff --git a/ark/plugins/cli7zplugin/cliplugin.h b/ark/plugins/cli7zplugin/cliplugin.h new file mode 100644 index 00000000..9f122e4a --- /dev/null +++ b/ark/plugins/cli7zplugin/cliplugin.h @@ -0,0 +1,61 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2009 Harald Hvaal + * Copyright (C) 2009-2010 Raphael Kubo da Costa + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#ifndef CLIPLUGIN_H +#define CLIPLUGIN_H + +#include "kerfuffle/cliinterface.h" + +class CliPlugin : public Kerfuffle::CliInterface +{ + Q_OBJECT + +public: + explicit CliPlugin(QObject *parent, const QVariantList & args); + virtual ~CliPlugin(); + +protected: + virtual Kerfuffle::ParameterList parameterList() const; + virtual bool readListLine(const QString &line); + +private: + enum ArchiveType { + ArchiveType7z = 0, + ArchiveTypeBZip2, + ArchiveTypeGZip, + ArchiveTypeTar, + ArchiveTypeZip + }; + + enum ReadState { + ReadStateHeader = 0, + ReadStateArchiveInformation, + ReadStateEntryInformation + }; + + ArchiveType m_archiveType; + Kerfuffle::ArchiveEntry m_currentArchiveEntry; + ReadState m_state; +}; + +#endif // CLIPLUGIN_H diff --git a/ark/plugins/cli7zplugin/kerfuffle_cli7z.desktop.cmake b/ark/plugins/cli7zplugin/kerfuffle_cli7z.desktop.cmake new file mode 100644 index 00000000..29b355af --- /dev/null +++ b/ark/plugins/cli7zplugin/kerfuffle_cli7z.desktop.cmake @@ -0,0 +1,69 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=Kerfuffle/Plugin +X-KDE-Library=kerfuffle_cli7z +X-KDE-PluginInfo-Author=Harald Hvaal +X-KDE-PluginInfo-Email=haraldhv@stud.ntnu.no +X-KDE-PluginInfo-Name=kerfuffle_7z +X-KDE-PluginInfo-Version=0.0.1 +X-KDE-PluginInfo-Website=http://www.kde.org +X-KDE-PluginInfo-License=GPLv2+ +X-KDE-Priority=120 +X-KDE-Kerfuffle-APIRevision=1 +X-KDE-Kerfuffle-ReadWrite=true +Name=7zip archive plugin +Name[ar]=ملحق أرشيف 7zip +Name[ast]=Complementu d'archivu comprimíu 7zip +Name[bg]=Приставка за архиви 7zip +Name[bs]=Priključak 7z arhiva +Name[ca]=Connector per arxius 7zip +Name[ca@valencia]=Connector per arxius 7zip +Name[cs]=Modul pro archiv 7zip +Name[da]=Plugin til 7zip-arkiver +Name[de]=7zip-Archiv-Modul +Name[el]=Πρόσθετο αρχειοθήκης 7zip +Name[en_GB]=7zip archive plugin +Name[es]=Complemento de archivo comprimido 7zip +Name[et]=7zip arhiivi plugin +Name[eu]=7zip artxiboen plugina +Name[fi]=7zip-pakkaustuki +Name[fr]=Module externe pour archive « 7zip » +Name[ga]=Breiseán cartlainne 7zip +Name[gl]=Extensión de arquivo de 7zip +Name[hr]=Arhivni priključak 7zip +Name[hu]=7zip modul +Name[ia]=Plugin de archivar 7zip +Name[id]=Pengaya arsip 7zip +Name[it]=estensione per archivi 7zip +Name[ja]=7zip アーカイブ用プラグイン +Name[kk]=7zip архив плагині +Name[km]=កម្មវិធី​ជំនួយ​ប័ណ្ណសារ 7zip +Name[ko]=7zip 압축 플러그인 +Name[lt]=7zip archyvo priedas +Name[lv]=7zip arhīvu spraudnis +Name[mr]=7ZIP संग्रह प्लगइन +Name[nb]=Programtillegg for 7zip-arkiv +Name[nds]=7zip-Archievmoduul +Name[nl]=7zip-archiefplug-in +Name[nn]=7zip-arkivtillegg +Name[pa]=7zip ਅਕਾਇਵ ਪਲੱਗਇਨ +Name[pl]=Wtyczka archiwów 7zip +Name[pt]='Plugin' de pacotes '7zip' +Name[pt_BR]=Plugin de arquivos 7zip +Name[ro]=Modul de arhivă 7zip +Name[ru]=Поддержка архивов 7zip +Name[sk]=Modul 7zip archívu +Name[sl]=Vstavek za arhive 7zip +Name[sq]=7zip arkiv plugin +Name[sr]=Прикључак 7зип архива +Name[sr@ijekavian]=Прикључак 7зип архива +Name[sr@ijekavianlatin]=Priključak 7zip arhiva +Name[sr@latin]=Priključak 7zip arhiva +Name[sv]=Insticksprogram för 7zip arkiv +Name[th]=ส่วนเสริมการจัดการแฟ้มจัดเก็บแบบ 7zip +Name[tr]=7zip arşivi eklentisi +Name[uk]=Додаток для архівів 7zip +Name[x-test]=xx7zip archive pluginxx +Name[zh_CN]=7zip 归档插件 +Name[zh_TW]=7zip 壓縮檔外掛程式 +MimeType=@SUPPORTED_CLI7Z_MIMETYPES@ diff --git a/ark/plugins/clilhaplugin/CMakeLists.txt b/ark/plugins/clilhaplugin/CMakeLists.txt new file mode 100644 index 00000000..07916306 --- /dev/null +++ b/ark/plugins/clilhaplugin/CMakeLists.txt @@ -0,0 +1,21 @@ +########### next target ############### + +set(SUPPORTED_CLILHA_MIMETYPES "application/lha;application/x-lha;application/maclha;") + +set(kerfuffle_cli_SRCS cliplugin.cpp) + +kde4_add_plugin(kerfuffle_clilha ${kerfuffle_cli_SRCS}) + +target_link_libraries(kerfuffle_clilha ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS} kerfuffle ) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_clilha.desktop.cmake + ${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_clilha.desktop +) + +########### install files ############### + +install(TARGETS kerfuffle_clilha DESTINATION ${PLUGIN_INSTALL_DIR} ) +install( FILES ${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_clilha.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + +set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}${SUPPORTED_CLILHA_MIMETYPES}" PARENT_SCOPE) diff --git a/ark/plugins/clilhaplugin/cliplugin.cpp b/ark/plugins/clilhaplugin/cliplugin.cpp new file mode 100644 index 00000000..5c2aa6e3 --- /dev/null +++ b/ark/plugins/clilhaplugin/cliplugin.cpp @@ -0,0 +1,144 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2011 Intzoglou Theofilos + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "cliplugin.h" + +#include "kerfuffle/kerfuffle_export.h" +#include +#include +#include + +using namespace Kerfuffle; + +CliPlugin::CliPlugin(QObject *parent, const QVariantList &args) + : CliInterface(parent, args), + m_status(Header), + m_firstLine(true) +{ +} + +CliPlugin::~CliPlugin() +{ +} + +ParameterList CliPlugin::parameterList() const +{ + static ParameterList p; + if (p.isEmpty()) { + p[CaptureProgress] = true; + p[ListProgram] = p[ExtractProgram] = p[DeleteProgram] = p[AddProgram] = QStringList() << QLatin1String("lha"); + + p[ListArgs] = QStringList() << QLatin1String("v") << QLatin1String("-v") << QLatin1String("$Archive"); + p[ExtractArgs] = QStringList() << QLatin1String("e") << QLatin1String("-v") << QLatin1String("$PreservePathSwitch") << QLatin1String("$Archive") << QLatin1String("$Files"); + + p[DeleteArgs] = QStringList() << QLatin1String("d") << QLatin1String("-v") << QLatin1String("$Archive") << QLatin1String("$Files"); + + p[FileExistsExpression] = QLatin1String("^(.+) OverWrite \\?"); + p[FileExistsMode] = 1; // Watch for messages in stdout + p[FileExistsInput] = QStringList() + << QLatin1String("Y") //overwrite + << QLatin1String("N") //skip + << QLatin1String("A") //overwrite all + << QLatin1String("S") //autoskip + ; + + p[AddArgs] = QStringList() << QLatin1String("a") << QLatin1String("-v") << QLatin1String("$Archive") << QLatin1String("$Files"); + + p[ExtractionFailedPatterns] = QStringList() << QLatin1String("Error"); + p[PreservePathSwitch] = QStringList() << QLatin1String( "" ) << QLatin1String( "-i" ); + } + return p; +} + +bool CliPlugin::readListLine(const QString &line) +{ + const QString m_headerString = QLatin1String("----------"); + + switch(m_status) { + case Header: + if (line.startsWith(m_headerString)) { + m_status = Entry; + m_firstLine = true; + } + break; + case Entry: + const QStringList entryList = line.split(QLatin1Char(' '), QString::SkipEmptyParts); + + if (m_firstLine) { // This line will contain the filename + if (entryList.count() == 8) { // End of entries + m_status = Header; + } + else { + m_internalId = line; + m_firstLine = false; + } + } + else { // This line contains the rest of the information + ArchiveEntry e; + + if (!entryList[0].startsWith(QLatin1Char('['))) { + e[Permissions] = entryList[0]; + } + + e[IsDirectory] = m_internalId.endsWith(QLatin1Char('/')); + m_entryFilename = m_internalId; + e[FileName] = m_entryFilename; + e[InternalID] = m_internalId; + + if (entryList.count() == 9) { // UID/GID is missing + e[CompressedSize] = entryList[1]; + e[Size] = entryList[2]; + e[Ratio] = entryList[3]; + e[Method] = entryList[4]; + e[CRC] = entryList[5]; + + QDateTime timestamp( + QDate::fromString(entryList[6], QLatin1String("yyyy-MM-dd")), + QTime::fromString(entryList[7], QLatin1String("HH:mm:ss"))); + e[Timestamp] = timestamp; + emit entry(e); + } + else if (entryList.count() == 10) { // All info is available + const QStringList ownerList = entryList[1].split(QLatin1Char('/')); // Separate uid from gui + e[Owner] = ownerList.at(0); + e[Group] = ownerList.at(1); + e[CompressedSize] = entryList[2]; + e[Size] = entryList[3]; + e[Ratio] = entryList[4]; + e[Method] = entryList[5]; + e[CRC] = entryList[6]; + + QDateTime timestamp( + QDate::fromString(entryList[7], QLatin1String("yyyy-MM-dd")), + QTime::fromString(entryList[8], QLatin1String("HH:mm:ss"))); + e[Timestamp] = timestamp; + emit entry(e); + } + + m_firstLine = true; + } + break; + } + return true; +} + +KERFUFFLE_EXPORT_PLUGIN(CliPlugin) + diff --git a/ark/plugins/clilhaplugin/cliplugin.h b/ark/plugins/clilhaplugin/cliplugin.h new file mode 100644 index 00000000..10aedad9 --- /dev/null +++ b/ark/plugins/clilhaplugin/cliplugin.h @@ -0,0 +1,49 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2011 Intzoglou Theofilos + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef CLIPLUGIN_H +#define CLIPLUGIN_H + +#include "kerfuffle/cliinterface.h" + +class CliPlugin : public Kerfuffle::CliInterface +{ + Q_OBJECT + +public: + explicit CliPlugin(QObject *parent, const QVariantList &args); + virtual ~CliPlugin(); + + virtual Kerfuffle::ParameterList parameterList() const; + + virtual bool readListLine(const QString &line); + +private: + enum { + Header = 0, + Entry + } m_status; + + QString m_entryFilename; + QString m_internalId; + bool m_firstLine; +}; + +#endif // CLIPLUGIN_H diff --git a/ark/plugins/clilhaplugin/kerfuffle_clilha.desktop.cmake b/ark/plugins/clilhaplugin/kerfuffle_clilha.desktop.cmake new file mode 100644 index 00000000..20ae991d --- /dev/null +++ b/ark/plugins/clilhaplugin/kerfuffle_clilha.desktop.cmake @@ -0,0 +1,62 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=Kerfuffle/Plugin +X-KDE-Library=kerfuffle_clilha +X-KDE-PluginInfo-Author=Theofilos Intzoglou +X-KDE-PluginInfo-Email=int.teo@gmail.com +X-KDE-PluginInfo-Name=kerfuffle_clilha +X-KDE-PluginInfo-Version=0.0.1 +X-KDE-PluginInfo-Website=http://www.kde.org +X-KDE-PluginInfo-License=GPLv2+ +X-KDE-Priority=100 +X-KDE-Kerfuffle-APIRevision=1 +X-KDE-Kerfuffle-ReadWrite=true +Name=lha archive plugin +Name[ar]=ملحَق أرشيفات IHA +Name[bg]=Приставка за архиви lha +Name[bs]=lha arhivni dodatak +Name[ca]=Connector per arxius LHA +Name[ca@valencia]=Connector per arxius LHA +Name[cs]=Modul pro archiv lha +Name[da]=Plugin til lha-arkiver +Name[de]=lha-Archiv-Modul +Name[el]=πρόσθετο αρχειοθήκης lha +Name[en_GB]=lha archive plugin +Name[es]=Complemento de archivo comprimido LHA +Name[et]=lha-arhiivi plugin +Name[eu]=LHA artxiboen plugina +Name[fi]=lha-pakkaustuki +Name[fr]=Module externe d'archive « lha » +Name[ga]=Breiseán cartlainne lha +Name[gl]=Complemento para arquivos LHA +Name[hu]=lha modul +Name[ia]=plugin de archivar lha +Name[it]=estensione per archivi lha +Name[ja]=lha アーカイブ用プラグイン +Name[kk]=lha архив плагині +Name[km]=កម្មវិធី​ជំនួយ​ប័ណ្ណសារ lha +Name[ko]=LHA 압축 플러그인 +Name[lt]=lha archyvo priedas +Name[mr]=lha संग्रह प्लगइन +Name[nb]=Programtillegg for lha-arkiv +Name[nds]=LHA-Archievmoduul +Name[nl]=lha-archiefplug-in +Name[pa]=lha ਅਕਾਇਵ ਪਲੱਗਇਨ +Name[pl]=Wtyczka archiwów lha +Name[pt]='Plugin' de pacotes LHA +Name[pt_BR]=Plugin de arquivos LHA +Name[ro]=Modul de arhivă lha +Name[ru]=Поддержка архивов LHA +Name[sk]=Modul lha archívu +Name[sl]=Vstavek za arhive lha +Name[sr]=Прикључак ЛХА архива +Name[sr@ijekavian]=Прикључак ЛХА архива +Name[sr@ijekavianlatin]=Priključak LHA arhiva +Name[sr@latin]=Priključak LHA arhiva +Name[sv]=Insticksprogram för LHA-arkiv +Name[tr]=lha arşiv eklentisi +Name[uk]=Додаток для архівів LHA +Name[x-test]=xxlha archive pluginxx +Name[zh_CN]=lha 归档插件 +Name[zh_TW]=lha 壓縮檔外掛程式 +MimeType=@SUPPORTED_CLILHA_MIMETYPES@ diff --git a/ark/plugins/cliplugin-example/CMakeLists.txt b/ark/plugins/cliplugin-example/CMakeLists.txt new file mode 100644 index 00000000..95836864 --- /dev/null +++ b/ark/plugins/cliplugin-example/CMakeLists.txt @@ -0,0 +1,7 @@ +set(kerfuffle_cli_SRCS cliplugin.cpp) +kde4_add_plugin(kerfuffle_cli ${kerfuffle_cli_SRCS}) +target_link_libraries(kerfuffle_cli ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS} kerfuffle) + +install(TARGETS kerfuffle_cli DESTINATION ${PLUGIN_INSTALL_DIR}) +install(FILES kerfuffle_cli.desktop DESTINATION ${SERVICES_INSTALL_DIR}) + diff --git a/ark/plugins/cliplugin-example/cliplugin.cpp b/ark/plugins/cliplugin-example/cliplugin.cpp new file mode 100644 index 00000000..c5ca4e08 --- /dev/null +++ b/ark/plugins/cliplugin-example/cliplugin.cpp @@ -0,0 +1,148 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2008 Claudio Bantaloukas + * Copyright (C) 2007 Henrique Pinto + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "cliplugin.h" +#include "kerfuffle/kerfuffle_export.h" + +#include + +#include +#include +#include + +CliPlugin::CliPlugin(QObject *parent, const QVariantList &args) + : CliInterface(parent, args), + m_isFirstLine(true), + m_incontent(false), + m_isPasswordProtected(false) +{ +} + +CliPlugin::~CliPlugin() +{ +} + +ParameterList CliPlugin::parameterList() const +{ + static ParameterList p; + + if (p.isEmpty()) { + p[CaptureProgress] = true; + p[ListProgram] = p[ExtractProgram] = p[DeleteProgram] = p[AddProgram] = QLatin1String("rar"); + + p[ListArgs] = QStringList() << QLatin1String("v") << QLatin1String("-c-") << QLatin1String("$Archive"); + p[ExtractArgs] = QStringList() << QLatin1String("-p-") << QLatin1String("$PreservePathSwitch") << QLatin1String("$PasswordSwitch") << QLatin1String("$RootNodeSwitch") << QLatin1String("$Archive") << QLatin1String("$Files"); + p[PreservePathSwitch] = QStringList() << QLatin1String("x") << QLatin1String("e"); + p[RootNodeSwitch] = QStringList() << QLatin1String("-ap$Path"); + p[PasswordSwitch] = QStringList() << QLatin1String("-p$Password"); + + p[DeleteArgs] = QStringList() << QLatin1String("d") << QLatin1String("$Archive") << QLatin1String("$Files"); + + p[FileExistsExpression] = QLatin1String("^(.+) already exists. Overwrite it"); + p[FileExistsInput] = QStringList() + << QLatin1String("Y") //overwrite + << QLatin1String("N") //skip + << QLatin1String("A") //overwrite all + << QLatin1String("E") //autoskip + << QLatin1String("Q") //cancel + ; + + p[AddArgs] = QStringList() << QLatin1String("a") << QLatin1String("$Archive") << QLatin1String("$Files"); + + p[WrongPasswordPatterns] = QStringList() << QLatin1String("password incorrect"); + p[ExtractionFailedPatterns] = QStringList() << QLatin1String("CRC failed"); + } + + return p; +} + +bool CliPlugin::readListLine(const QString &line) +{ + const QString m_headerString = QLatin1String("-----------------------------------------"); + + // skip the heading + if (!m_incontent) { + if (line.startsWith(m_headerString)) { + m_incontent = true; + } + return true; + } + + // catch final line + if (line.startsWith(m_headerString)) { + m_incontent = false; + return true; + } + + // rar gives one line for the filename and a line after it with some file properties + if (m_isFirstLine) { + m_internalId = line.trimmed(); + //m_entryFilename.chop(1); // handle newline + if (!m_internalId.isEmpty() && m_internalId.at(0) == QLatin1Char('*')) { + m_isPasswordProtected = true; + m_internalId.remove(0, 1); // and the spaces in front + } else + m_isPasswordProtected = false; + + m_isFirstLine = false; + return true; + } + + QStringList fileprops = line.split(QLatin1Char(' '), QString::SkipEmptyParts); + m_internalId = QDir::fromNativeSeparators(m_internalId); + bool isDirectory = (bool)(fileprops[ 5 ].contains(QLatin1Char('d'), Qt::CaseInsensitive)); + + QDateTime ts(QDate::fromString(fileprops[ 3 ], QLatin1String("dd-MM-yy")), + QTime::fromString(fileprops[ 4 ], QLatin1String("hh:mm"))); + // rar output date with 2 digit year but QDate takes is as 19?? + // let's take 1950 is cut-off; similar to KDateTime + if (ts.date().year() < 1950) { + ts = ts.addYears(100); + } + + m_entryFilename = m_internalId; + if (isDirectory && !m_internalId.endsWith(QLatin1Char('/'))) { + m_entryFilename += QLatin1Char('/'); + } + + //kDebug() << m_entryFilename << " : " << fileprops; + ArchiveEntry e; + e[ FileName ] = m_entryFilename; + e[ InternalID ] = m_internalId; + e[ Size ] = fileprops[ 0 ]; + e[ CompressedSize] = fileprops[ 1 ]; + e[ Ratio ] = fileprops[ 2 ]; + e[ Timestamp ] = ts; + e[ IsDirectory ] = isDirectory; + e[ Permissions ] = fileprops[ 5 ].remove(0, 1); + e[ CRC ] = fileprops[ 6 ]; + e[ Method ] = fileprops[ 7 ]; + e[ Version ] = fileprops[ 8 ]; + e[ IsPasswordProtected] = m_isPasswordProtected; + kDebug() << "Added entry: " << e; + + emit entry(e); + m_isFirstLine = true; + return true; +} + +KERFUFFLE_EXPORT_PLUGIN(CliPlugin) diff --git a/ark/plugins/cliplugin-example/cliplugin.h b/ark/plugins/cliplugin-example/cliplugin.h new file mode 100644 index 00000000..21e935d6 --- /dev/null +++ b/ark/plugins/cliplugin-example/cliplugin.h @@ -0,0 +1,45 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2008 Claudio Bantaloukas + * Copyright (C) 2007 Henrique Pinto + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef CLIPLUGIN_H +#define CLIPLUGIN_H + +#include "kerfuffle/cliinterface.h" + +using namespace Kerfuffle; + +class CliPlugin: public CliInterface +{ +public: + explicit CliPlugin(QObject *parent = 0, const QVariantList &args = QVariantList()); + virtual ~CliPlugin(); + + virtual ParameterList parameterList() const; + bool readListLine(const QString &line); + +private: + bool m_isFirstLine, m_incontent, m_isPasswordProtected; + QString m_entryFilename, m_internalId; + +}; + +#endif // CLIPLUGIN_H diff --git a/ark/plugins/cliplugin-example/kerfuffle_cli.desktop b/ark/plugins/cliplugin-example/kerfuffle_cli.desktop new file mode 100644 index 00000000..51de02de --- /dev/null +++ b/ark/plugins/cliplugin-example/kerfuffle_cli.desktop @@ -0,0 +1,66 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=Kerfuffle/Plugin +X-KDE-Library=TODO kerfuffle_cli +X-KDE-PluginInfo-Author=TODO Your name +X-KDE-PluginInfo-Email=TODO Your email +X-KDE-PluginInfo-Name=TODO kerfuffle_cli +X-KDE-PluginInfo-Version=0.0.1 +X-KDE-PluginInfo-Website=http://www.kde.org +X-KDE-PluginInfo-License=GPLv2+ +X-KDE-Priority=100 +X-KDE-Kerfuffle-APIRevision=1 +X-KDE-Kerfuffle-ReadWrite=true +Name=TODO archive plugin +Name[ar]=ملحق أرشيف TODO +Name[ast]=Complementu d'archivu comprimíu 'TODO' +Name[bs]=(URADI) priključak arhiva +Name[ca]=Connector per arxius TODO +Name[ca@valencia]=Connector per arxius TODO +Name[cs]=Modul pro archiv TODO +Name[da]=Plugin til TODO-arkiver +Name[de]=TODO-Archiv-Modul +Name[el]=πρόσθετο αρχειοθήκης προς υλοποίηση +Name[en_GB]=TODO archive plugin +Name[es]=Complemento de archivo comprimido XXX +Name[et]=TODO arhiivi plugin +Name[eu]=TODO artxiboen plugina +Name[fi]=TODO-pakkaustuki +Name[fr]=Module d'archive « À faire » +Name[ga]=Breiseán cartlainne TODO +Name[gl]=Extensión de arquivo TODO +Name[hr]=Arhivni priključak TODO +Name[hu]=TODO modul +Name[ia]=plugin de archivar DEFACER (TODO) +Name[id]=Pengaya arsip TODO +Name[it]=estensione per archivi TODO +Name[kk]=TODO архив плагині +Name[km]=កម្មវិធី​ជំនួយ​ប័ណ្ណសារ​របស់​ការងារ​ត្រូវ​ធ្វើ +Name[ko]=TODO 압축 플러그인 +Name[lt]=PADARYTI archyvų priedas +Name[lv]=TODO arhīvu spraudnis +Name[mr]=TODO संग्रह प्लगइन +Name[nb]=Programtillegg for gjøremålsarkiv +Name[nds]=TODO-Archievmoduul +Name[nl]=TODO-archiefplug-in +Name[nn]=TODO-arkivtillegg +Name[pl]=Wtyczka archiwów do zrobienia +Name[pt]='Plugin' de pacotes POR FAZER +Name[pt_BR]=Plugin de arquivos A FAZER +Name[ro]=Modul de arhivă TODO +Name[ru]=TODO модуль архивирования +Name[sk]=TODO modul archívu +Name[sl]=Vstavek za arhive NAREDI +Name[sq]=TODO arkiv plugin +Name[sr]=Прикључак TODO архива +Name[sr@ijekavian]=Прикључак TODO архива +Name[sr@ijekavianlatin]=Priključak TODO arhiva +Name[sr@latin]=Priključak TODO arhiva +Name[sv]=Insticksprogram för ATT GÖRA arkiv +Name[th]=ส่วนเสริมการจัดการแฟ้มจัดเก็บแบบ TODO +Name[tr]=TODO arşivi eklentisi +Name[uk]=Ще не створений додаток архівів +Name[x-test]=xxTODO archive pluginxx +Name[zh_CN]=TODO 归档插件 +Name[zh_TW]=TODO 歸檔外掛程式 +MimeType=TODO; diff --git a/ark/plugins/clirarplugin/CMakeLists.txt b/ark/plugins/clirarplugin/CMakeLists.txt new file mode 100644 index 00000000..f0ec0d0d --- /dev/null +++ b/ark/plugins/clirarplugin/CMakeLists.txt @@ -0,0 +1,23 @@ +########### next target ############### + +set(SUPPORTED_CLIRAR_MIMETYPES "application/x-rar;") + +set(kerfuffle_clirar_SRCS cliplugin.cpp) + +kde4_add_plugin(kerfuffle_clirar ${kerfuffle_clirar_SRCS}) + +target_link_libraries(kerfuffle_clirar ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS} kerfuffle ) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_clirar.desktop.cmake + ${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_clirar.desktop +) + +########### install files ############### + +install(TARGETS kerfuffle_clirar DESTINATION ${PLUGIN_INSTALL_DIR} ) +install( FILES ${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_clirar.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + +add_subdirectory(tests) + +set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}${SUPPORTED_CLIRAR_MIMETYPES}" PARENT_SCOPE) diff --git a/ark/plugins/clirarplugin/cliplugin.cpp b/ark/plugins/clirarplugin/cliplugin.cpp new file mode 100644 index 00000000..f5f7b184 --- /dev/null +++ b/ark/plugins/clirarplugin/cliplugin.cpp @@ -0,0 +1,362 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2009 Harald Hvaal + * Copyright (C) 2010-2011,2014 Raphael Kubo da Costa + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "cliplugin.h" +#include "kerfuffle/cliinterface.h" +#include "kerfuffle/kerfuffle_export.h" + +#include + +#include +#include +#include +#include + +using namespace Kerfuffle; + +CliPlugin::CliPlugin(QObject *parent, const QVariantList& args) + : CliInterface(parent, args) + , m_parseState(ParseStateColumnDescription1) + , m_isPasswordProtected(false) + , m_remainingIgnoredSubHeaderLines(0) + , m_remainingIgnoredDetailsLines(0) + , m_isUnrarFree(false) + , m_isUnrarVersion5(false) +{ +} + +CliPlugin::~CliPlugin() +{ +} + +// #272281: the proprietary unrar program does not like trailing '/'s +// in directories passed to it when extracting only part of +// the files in an archive. +QString CliPlugin::escapeFileName(const QString &fileName) const +{ + if (fileName.endsWith(QLatin1Char('/'))) { + return fileName.left(fileName.length() - 1); + } + + return fileName; +} + +ParameterList CliPlugin::parameterList() const +{ + static ParameterList p; + + if (p.isEmpty()) { + p[CaptureProgress] = true; + p[ListProgram] = p[ExtractProgram] = QStringList() << QLatin1String( "unrar" ); + p[DeleteProgram] = p[AddProgram] = QStringList() << QLatin1String( "rar" ); + + p[ListArgs] = QStringList() << QLatin1String( "vt" ) << QLatin1String( "-c-" ) << QLatin1String( "-v" ) << QLatin1String( "$Archive" ); + p[ExtractArgs] = QStringList() << QLatin1String( "-kb" ) << QLatin1String( "-p-" ) + << QLatin1String( "$PreservePathSwitch" ) + << QLatin1String( "$PasswordSwitch" ) + << QLatin1String( "$RootNodeSwitch" ) + << QLatin1String( "$Archive" ) + << QLatin1String( "$Files" ); + p[PreservePathSwitch] = QStringList() << QLatin1String( "x" ) << QLatin1String( "e" ); + p[RootNodeSwitch] = QStringList() << QLatin1String( "-ap$Path" ); + p[PasswordSwitch] = QStringList() << QLatin1String( "-p$Password" ); + + p[DeleteArgs] = QStringList() << QLatin1String( "d" ) << QLatin1String( "$Archive" ) << QLatin1String( "$Files" ); + + p[FileExistsExpression] = QLatin1String( "^(.+) already exists. Overwrite it" ); + p[FileExistsInput] = QStringList() + << QLatin1String( "Y" ) //overwrite + << QLatin1String( "N" ) //skip + << QLatin1String( "A" ) //overwrite all + << QLatin1String( "E" ) //autoskip + << QLatin1String( "Q" ) //cancel + ; + + p[AddArgs] = QStringList() << QLatin1String( "a" ) << QLatin1String( "$Archive" ) << QLatin1String( "$Files" ); + + p[PasswordPromptPattern] = QLatin1String("Enter password \\(will not be echoed\\) for"); + + p[WrongPasswordPatterns] = QStringList() << QLatin1String("password incorrect") << QLatin1String("wrong password"); + p[ExtractionFailedPatterns] = QStringList() << QLatin1String( "CRC failed" ) << QLatin1String( "Cannot find volume" ); + } + + return p; +} + +bool CliPlugin::readListLine(const QString &line) +{ + static const QLatin1String headerString("----------------------"); + static const QLatin1String subHeaderString("Data header type: "); + static const QLatin1String columnDescription1String(" Size Packed Ratio Date Time Attr CRC Meth Ver"); + static const QLatin1String columnDescription2String(" Host OS Solid Old"); // Only present in unrar-nonfree + + if (m_isUnrarVersion5) { + int colonPos = line.indexOf(QLatin1Char(':')); + if (colonPos == -1) { + if (m_entryFileName.isEmpty()) { + return true; + } + ArchiveEntry e; + + QString compressionRatio = m_entryDetails.value(QLatin1String("ratio")); + compressionRatio.chop(1); // Remove the '%' + + QString time = m_entryDetails.value(QLatin1String("mtime")); + // FIXME unrar 5 beta 8 seems to lack the seconds, or the trailing ,000 is not the milliseconds + QDateTime ts = QDateTime::fromString(time, QLatin1String("yyyy-MM-dd HH:mm,zzz")); + + bool isDirectory = m_entryDetails.value(QLatin1String("type")) == QLatin1String("Directory"); + if (isDirectory && !m_entryFileName.endsWith(QLatin1Char( '/' ))) { + m_entryFileName += QLatin1Char( '/' ); + } + + QString compression = m_entryDetails.value(QLatin1String("compression")); + int optionPos = compression.indexOf(QLatin1Char('-')); + if (optionPos != -1) { + e[Method] = compression.mid(optionPos); + e[Version] = compression.left(optionPos).trimmed(); + } else { + // no method specified + e[Method].clear(); + e[Version] = compression; + } + + m_isPasswordProtected = m_entryDetails.value(QLatin1String("flags")).contains(QLatin1String("encrypted")); + + e[FileName] = m_entryFileName; + e[InternalID] = m_entryFileName; + e[Size] = m_entryDetails.value(QLatin1String("size")); + e[CompressedSize] = m_entryDetails.value(QLatin1String("packed size")); + e[Ratio] = compressionRatio; + e[Timestamp] = ts; + e[IsDirectory] = isDirectory; + e[Permissions] = m_entryDetails.value(QLatin1String("attributes")); + e[CRC] = m_entryDetails.value(QLatin1String("crc32")); + e[IsPasswordProtected] = m_isPasswordProtected; + kDebug() << "Added entry: " << e; + + emit entry(e); + + m_entryFileName.clear(); + + return true; + } + + QString key = line.left(colonPos).trimmed().toLower(); + QString value = line.mid(colonPos + 2); + + if (key == QLatin1String("name")) { + m_entryFileName = value; + m_entryDetails.clear(); + return true; + } + + // in multivolume archives, the split CRC32 is denoted specially + if (key == QLatin1String("pack-crc32")) { + key = key.mid(5); + } + + m_entryDetails.insert(key, value); + + return true; + } + + switch (m_parseState) + { + case ParseStateColumnDescription1: + if (line.startsWith(QLatin1String("Details:"))) { + m_isUnrarVersion5 = true; + setListEmptyLines(true); + // no previously detected entry + m_entryFileName.clear(); + } + if (line.startsWith(columnDescription1String)) { + m_parseState = ParseStateColumnDescription2; + } + + break; + + case ParseStateColumnDescription2: + // #243273: We need a way to differentiate unrar and unrar-free, + // as their output for the "vt" option is different. + // Currently, we differ them by checking if "vt" produces + // two lines of column names before the header string, as + // only unrar does that (unrar-free always outputs one line + // for column names regardless of how verbose we tell it to + // be). + if (line.startsWith(columnDescription2String)) { + m_parseState = ParseStateHeader; + } else if (line.startsWith(headerString)) { + m_parseState = ParseStateEntryFileName; + m_isUnrarFree = true; + } + + break; + + case ParseStateHeader: + if (line.startsWith(headerString)) { + m_parseState = ParseStateEntryFileName; + } + + break; + + case ParseStateEntryFileName: + if (m_remainingIgnoredSubHeaderLines > 0) { + --m_remainingIgnoredSubHeaderLines; + return true; + } + + // #242071: The RAR file format has the concept of service headers, + // such as CMT (comments), STM (NTFS alternate data streams) + // and RR (recovery record). These service headers do no + // interest us, and ignoring them seems harmless (at least + // 7zip and WinRAR do not show them either). + if (line.startsWith(subHeaderString)) { + // subHeaderString's length is 18 + const QString subHeaderType(line.mid(18)); + + // XXX: If we ever support archive comments, this code must + // be changed, because the comments will be shown after + // a CMT subheader and will have an arbitrary number of lines + if (subHeaderType == QLatin1String("STM")) { + m_remainingIgnoredSubHeaderLines = 4; + } else { + m_remainingIgnoredSubHeaderLines = 3; + } + + kDebug() << "Found a subheader of type" << subHeaderType; + kDebug() << "The next" << m_remainingIgnoredSubHeaderLines + << "lines will be ignored"; + + return true; + } else if (line.startsWith(headerString)) { + m_parseState = ParseStateHeader; + + return true; + } + + m_isPasswordProtected = (line.at(0) == QLatin1Char( '*' )); + + // Start from 1 because the first character is either ' ' or '*' + m_entryFileName = QDir::fromNativeSeparators(line.mid(1)); + + m_parseState = ParseStateEntryDetails; + + break; + + case ParseStateEntryIgnoredDetails: + if (m_remainingIgnoredDetailsLines > 0) { + --m_remainingIgnoredDetailsLines; + return true; + } + m_parseState = ParseStateEntryFileName; + + break; + + case ParseStateEntryDetails: + if (line.startsWith(headerString)) { + m_parseState = ParseStateHeader; + return true; + } + + const QStringList details = line.split(QLatin1Char( ' ' ), + QString::SkipEmptyParts); + + QDateTime ts(QDate::fromString(details.at(3), + QLatin1String("dd-MM-yy")), + QTime::fromString(details.at(4), + QLatin1String("hh:mm"))); + + // unrar outputs dates with a 2-digit year but QDate takes it as 19?? + // let's take 1950 is cut-off; similar to KDateTime + if (ts.date().year() < 1950) { + ts = ts.addYears(100); + } + + bool isDirectory = ((details.at(5).at(0) == QLatin1Char( 'd' )) || + (details.at(5).at(1) == QLatin1Char( 'D' ))); + if (isDirectory && !m_entryFileName.endsWith(QLatin1Char( '/' ))) { + m_entryFileName += QLatin1Char( '/' ); + } + + // If the archive is a multivolume archive, a string indicating + // whether the archive's position in the volume is displayed + // instead of the compression ratio. + QString compressionRatio = details.at(2); + if ((compressionRatio == QLatin1String("<--")) || + (compressionRatio == QLatin1String("<->")) || + (compressionRatio == QLatin1String("-->"))) { + compressionRatio = QLatin1Char( '0' ); + } else { + compressionRatio.chop(1); // Remove the '%' + } + + // TODO: + // - Permissions differ depending on the system the entry was added + // to the archive. + // - unrar reports the ratio as ((compressed size * 100) / size); + // we consider ratio as (100 * ((size - compressed size) / size)). + ArchiveEntry e; + e[FileName] = m_entryFileName; + e[InternalID] = m_entryFileName; + e[Size] = details.at(0); + e[CompressedSize] = details.at(1); + e[Ratio] = compressionRatio; + e[Timestamp] = ts; + e[IsDirectory] = isDirectory; + e[Permissions] = details.at(5); + e[CRC] = details.at(6); + e[Method] = details.at(7); + e[Version] = details.at(8); + e[IsPasswordProtected] = m_isPasswordProtected; + kDebug() << "Added entry: " << e; + + // #314297: When RAR 3.x and RAR 4.x list a symlink, they output an + // extra line after the "Host OS/Solid/Old" one mentioning the + // target of the symlink in question. We are not interested in + // this line at the moment, so we just tell the parser to skip + // it. + if (e[Permissions].toString().startsWith(QLatin1Char('l'))) { + m_remainingIgnoredDetailsLines = 1; + } else { + m_remainingIgnoredDetailsLines = 0; + } + + emit entry(e); + + // #243273: unrar-free does not output the third file entry line, + // skip directly to parsing a new entry. + if (m_isUnrarFree) { + m_parseState = ParseStateEntryFileName; + } else { + m_parseState = ParseStateEntryIgnoredDetails; + } + + break; + } + + return true; +} + +KERFUFFLE_EXPORT_PLUGIN(CliPlugin) + +#include "cliplugin.moc" diff --git a/ark/plugins/clirarplugin/cliplugin.h b/ark/plugins/clirarplugin/cliplugin.h new file mode 100644 index 00000000..e11fca40 --- /dev/null +++ b/ark/plugins/clirarplugin/cliplugin.h @@ -0,0 +1,65 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2009 Harald Hvaal + * Copyright (C) 2009-2010 Raphael Kubo da Costa + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef CLIPLUGIN_H +#define CLIPLUGIN_H + +#include "kerfuffle/cliinterface.h" + +class CliPlugin : public Kerfuffle::CliInterface +{ + Q_OBJECT + +public: + explicit CliPlugin(QObject *parent, const QVariantList & args); + + virtual ~CliPlugin(); + + virtual QString escapeFileName(const QString &fileName) const; + + virtual Kerfuffle::ParameterList parameterList() const; + + virtual bool readListLine(const QString &line); + +private: + enum { + ParseStateColumnDescription1 = 0, + ParseStateColumnDescription2, + ParseStateHeader, + ParseStateEntryFileName, + ParseStateEntryDetails, + ParseStateEntryIgnoredDetails + } m_parseState; + + QString m_entryFileName; + QHash m_entryDetails; + + bool m_isPasswordProtected; + + int m_remainingIgnoredSubHeaderLines; + int m_remainingIgnoredDetailsLines; + + bool m_isUnrarFree; + bool m_isUnrarVersion5; +}; + +#endif // CLIPLUGIN_H diff --git a/ark/plugins/clirarplugin/kerfuffle_clirar.desktop.cmake b/ark/plugins/clirarplugin/kerfuffle_clirar.desktop.cmake new file mode 100644 index 00000000..3091d9d3 --- /dev/null +++ b/ark/plugins/clirarplugin/kerfuffle_clirar.desktop.cmake @@ -0,0 +1,69 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=Kerfuffle/Plugin +X-KDE-Library=kerfuffle_clirar +X-KDE-PluginInfo-Author=Harald Hvaal +X-KDE-PluginInfo-Email=haraldhv@stud.ntnu.no +X-KDE-PluginInfo-Name=kerfuffle_clirar +X-KDE-PluginInfo-Version=0.0.1 +X-KDE-PluginInfo-Website=http://www.kde.org +X-KDE-PluginInfo-License=GPLv2+ +X-KDE-Priority=120 +X-KDE-Kerfuffle-APIRevision=1 +X-KDE-Kerfuffle-ReadWrite=true +Name=RAR archive plugin +Name[ar]=ملحق أرشيف RAR +Name[ast]=Complementu d'archivu comprimíu RAR +Name[bg]=Приставка за архиви RAR +Name[bs]=Priključak RAR arhiva +Name[ca]=Connector per arxius RAR +Name[ca@valencia]=Connector per arxius RAR +Name[cs]=Modul pro archiv RAR +Name[da]=Plugin til RAR-arkiver +Name[de]=RAR-Archiv-Modul +Name[el]=πρόσθετο αρχειοθήκης RAR +Name[en_GB]=RAR archive plugin +Name[es]=Complemento de archivo comprimido RAR +Name[et]=RAR-arhiivi plugin +Name[eu]=RAR artxiboen plugina +Name[fi]=RAR-pakkaustuki +Name[fr]=Module externe d'archive « RAR » +Name[ga]=Breiseán cartlainne RAR +Name[gl]=Extensión de arquivo RAR +Name[hr]=Arhivni priključak RAR +Name[hu]=RAR modul +Name[ia]=plugin de archivar RAR +Name[id]=Pengaya arsip RAR +Name[it]=estensione per archivi RAR +Name[ja]=RAR アーカイブ用プラグイン +Name[kk]=RAR архив плагині +Name[km]=កម្មវិធី​ជំនួយ​ប័ណ្ណសារ RAR +Name[ko]=RAR 압축 플러그인 +Name[lt]=RAR archyvo priedas +Name[lv]=RAR arhīvu spraudnis +Name[mr]=RAR संग्रह प्लगइन +Name[nb]=Programtillegg for RAR-arkiv +Name[nds]=RAR-Archievmoduul +Name[nl]=RAR-archiefplug-in +Name[nn]=RAR-arkivtillegg +Name[pa]=RAR ਅਕਾਇਵ ਪਲੱਗਇਨ +Name[pl]=Wtyczka archiwów RAR +Name[pt]='Plugin' de pacotes RAR +Name[pt_BR]=Plugin de arquivos RAR +Name[ro]=Modul de arhivă RAR +Name[ru]=Поддержка архивов RAR +Name[sk]=Modul RAR archívu +Name[sl]=Vstavek za arhive RAR +Name[sq]=RAR arkiv plugin +Name[sr]=Прикључак РАР архива +Name[sr@ijekavian]=Прикључак РАР архива +Name[sr@ijekavianlatin]=Priključak RAR arhiva +Name[sr@latin]=Priključak RAR arhiva +Name[sv]=Insticksprogram för RAR-arkiv +Name[th]=ส่วนเสริมการจัดการแฟ้มจัดเก็บบีบอัดแบบ RAR +Name[tr]=RAR arşivi eklentisi +Name[uk]=Додаток для архівів RAR +Name[x-test]=xxRAR archive pluginxx +Name[zh_CN]=RAR 归档插件 +Name[zh_TW]=RAR 壓縮檔外掛程式 +MimeType=@SUPPORTED_CLIRAR_MIMETYPES@ diff --git a/ark/plugins/clirarplugin/tests/CMakeLists.txt b/ark/plugins/clirarplugin/tests/CMakeLists.txt new file mode 100644 index 00000000..5466c2f8 --- /dev/null +++ b/ark/plugins/clirarplugin/tests/CMakeLists.txt @@ -0,0 +1,4 @@ +set(RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +kde4_add_unit_test(clirartest NOGUI clirartest.cpp ../cliplugin.cpp) +target_link_libraries(clirartest kerfuffle ${QT_QTTEST_LIBRARY} ${KDE4_KPARTS_LIBRARY}) diff --git a/ark/plugins/clirarplugin/tests/clirartest.cpp b/ark/plugins/clirarplugin/tests/clirartest.cpp new file mode 100644 index 00000000..622dd6c7 --- /dev/null +++ b/ark/plugins/clirarplugin/tests/clirartest.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2011,2014 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "clirartest.h" +#include "../cliplugin.h" + +#include + +#include +#include + +QTEST_KDEMAIN_CORE(CliRarTest) + +using namespace Kerfuffle; + +/* + * Check that the plugin will not crash when reading corrupted archives, which + * have lines such as "Unexpected end of archive" or "??? - the file header is + * corrupt" instead of a file name and the header string after it. + * + * See bug 262857 and commit 2042997013432cdc6974f5b26d39893a21e21011. + */ +void CliRarTest::testReadCorruptedArchive() +{ + QVariantList args; + args.append(QLatin1String("DummyArchive.rar")); + + CliPlugin *rarPlugin = new CliPlugin(this, args); + Q_ASSERT(rarPlugin->open()); + + QFile unrarOutput(QLatin1String(KDESRCDIR "data/testReadCorruptedArchive.txt")); + Q_ASSERT(unrarOutput.open(QIODevice::ReadOnly)); + + QTextStream unrarStream(&unrarOutput); + while (!unrarStream.atEnd()) { + const QString line(unrarStream.readLine()); + Q_ASSERT(rarPlugin->readListLine(line)); + } + + rarPlugin->deleteLater(); +} + +/* + * Bug 314297: do not crash when a RAR archive has a symlink. + */ +void CliRarTest::testParseSymlink() +{ + QVariantList args; + args.append(QLatin1String("DummyArchive.rar")); + + CliPlugin *rarPlugin = new CliPlugin(this, args); + Q_ASSERT(rarPlugin->open()); + + QFile unrarOutput(QLatin1String(KDESRCDIR "data/testReadArchiveWithSymlink.txt")); + Q_ASSERT(unrarOutput.open(QIODevice::ReadOnly)); + + QTextStream unrarStream(&unrarOutput); + while (!unrarStream.atEnd()) { + const QString line(unrarStream.readLine()); + Q_ASSERT(rarPlugin->readListLine(line)); + } + + rarPlugin->deleteLater(); +} diff --git a/ark/plugins/clirarplugin/tests/clirartest.h b/ark/plugins/clirarplugin/tests/clirartest.h new file mode 100644 index 00000000..044b8312 --- /dev/null +++ b/ark/plugins/clirarplugin/tests/clirartest.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CLIRARTEST_H +#define CLIRARTEST_H + +#include + +class CliRarTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void testReadCorruptedArchive(); + void testParseSymlink(); +}; + +#endif diff --git a/ark/plugins/clirarplugin/tests/data/testReadArchiveWithSymlink.txt b/ark/plugins/clirarplugin/tests/data/testReadArchiveWithSymlink.txt new file mode 100644 index 00000000..e6db584d --- /dev/null +++ b/ark/plugins/clirarplugin/tests/data/testReadArchiveWithSymlink.txt @@ -0,0 +1,22 @@ + +UNRAR 4.20 (iconv) freeware Copyright (c) 1993-2012 Alexander Roshal + +Archive foo.rar + +Pathname/Comment + Size Packed Ratio Date Time Attr CRC Meth Ver + Host OS Solid Old +------------------------------------------------------------------------------- + foo/hello1 + 6 16 266% 12-02-14 23:15 -rw-r--r-- 363A3020 m3b 2.9 + Unix No No + foo/hello2 + 6 6 100% 12-02-14 23:15 lrwxr-xr-x 8731D904 m0b 2.0 + Unix No No + --> hello1 + foo + 0 0 0% 12-02-14 23:15 drwxr-xr-x 00000000 m0 2.0 + Unix No No +------------------------------------------------------------------------------- + 3 12 22 183% + diff --git a/ark/plugins/clirarplugin/tests/data/testReadCorruptedArchive.txt b/ark/plugins/clirarplugin/tests/data/testReadCorruptedArchive.txt new file mode 100644 index 00000000..be702812 --- /dev/null +++ b/ark/plugins/clirarplugin/tests/data/testReadCorruptedArchive.txt @@ -0,0 +1,23 @@ +UNRAR 3.90 beta 3 freeware Copyright (c) 1993-2009 Alexander Roshal + +Volume foo.r45 + +Recovery record is present + +Pathname/Comment + Size Packed Ratio Date Time Attr CRC Meth Ver + Host OS Solid Old +------------------------------------------------------------------------------- + some-file.ext + 732522496 14851208 <-> 29-10-10 20:47 .....A. 0868E5EA m0g 2.0 + Win95/NT No No +Data header type: RR + RR + 148638 148638 100% 00-00-80 00:00 .....B 3622BF05 m0a 2.9 + Win95/NT No No +??? - the file header is corrupt +------------------------------------------------------------------------------- + 0 0 14851208 0% volume 46 + + 1 732522496 732522496 100% + diff --git a/ark/plugins/clizipplugin/CMakeLists.txt b/ark/plugins/clizipplugin/CMakeLists.txt new file mode 100644 index 00000000..5e180fb9 --- /dev/null +++ b/ark/plugins/clizipplugin/CMakeLists.txt @@ -0,0 +1,21 @@ +########### next target ############### + +set(SUPPORTED_CLIZIP_MIMETYPES "application/x-java-archive;application/zip;") + +set(kerfuffle_clizip_SRCS cliplugin.cpp) + +kde4_add_plugin(kerfuffle_clizip ${kerfuffle_clizip_SRCS}) + +target_link_libraries(kerfuffle_clizip ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS} kerfuffle ) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_clizip.desktop.cmake + ${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_clizip.desktop +) + +########### install files ############### + +install(TARGETS kerfuffle_clizip DESTINATION ${PLUGIN_INSTALL_DIR} ) +install( FILES ${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_clizip.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + +set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}${SUPPORTED_CLIZIP_MIMETYPES}" PARENT_SCOPE) diff --git a/ark/plugins/clizipplugin/cliplugin.cpp b/ark/plugins/clizipplugin/cliplugin.cpp new file mode 100644 index 00000000..1be2a10f --- /dev/null +++ b/ark/plugins/clizipplugin/cliplugin.cpp @@ -0,0 +1,143 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2009 Harald Hvaal + * Copyright (C) 2009-2011 Raphael Kubo da Costa + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "cliplugin.h" +#include "kerfuffle/cliinterface.h" +#include "kerfuffle/kerfuffle_export.h" + +#include + +#include +#include +#include +#include +#include +#include + +using namespace Kerfuffle; + +CliPlugin::CliPlugin(QObject *parent, const QVariantList & args) + : CliInterface(parent, args) + , m_status(Header) +{ +} + +CliPlugin::~CliPlugin() +{ +} + +// #208091: infozip applies special meanings to some characters, so we +// need to escape them with backslashes.see match.c in +// infozip's source code +QString CliPlugin::escapeFileName(const QString &fileName) const +{ + const QString escapedCharacters(QLatin1String("[]*?^-\\!")); + + QString quoted; + const int len = fileName.length(); + const QLatin1Char backslash('\\'); + quoted.reserve(len * 2); + + for (int i = 0; i < len; ++i) { + if (escapedCharacters.contains(fileName.at(i))) { + quoted.append(backslash); + } + + quoted.append(fileName.at(i)); + } + + return quoted; +} + +ParameterList CliPlugin::parameterList() const +{ + static ParameterList p; + + if (p.isEmpty()) { + p[CaptureProgress] = false; + p[ListProgram] = QStringList() << QLatin1String( "zipinfo" ); + p[ExtractProgram] = QStringList() << QLatin1String( "unzip" ); + p[DeleteProgram] = p[AddProgram] = QStringList() << QLatin1String( "zip" ); + + p[ListArgs] = QStringList() << QLatin1String( "-l" ) << QLatin1String( "-T" ) << QLatin1String( "$Archive" ); + p[ExtractArgs] = QStringList() << QLatin1String( "$PreservePathSwitch" ) << QLatin1String( "$PasswordSwitch" ) << QLatin1String( "$Archive" ) << QLatin1String( "$Files" ); + p[PreservePathSwitch] = QStringList() << QLatin1String( "" ) << QLatin1String( "-j" ); + p[PasswordSwitch] = QStringList() << QLatin1String( "-P$Password" ); + + p[DeleteArgs] = QStringList() << QLatin1String( "-d" ) << QLatin1String( "$Archive" ) << QLatin1String( "$Files" ); + + p[FileExistsExpression] = QLatin1String( "^replace (.+)\\?" ); + p[FileExistsInput] = QStringList() + << QLatin1String( "y" ) //overwrite + << QLatin1String( "n" ) //skip + << QLatin1String( "A" ) //overwrite all + << QLatin1String( "N" ) //autoskip + ; + + p[AddArgs] = QStringList() << QLatin1String( "-r" ) << QLatin1String( "$Archive" ) << QLatin1String( "$Files" ); + + p[PasswordPromptPattern] = QLatin1String(" password: "); + p[WrongPasswordPatterns] = QStringList() << QLatin1String( "incorrect password" ); + //p[ExtractionFailedPatterns] = QStringList() << "CRC failed"; + } + return p; +} + +bool CliPlugin::readListLine(const QString &line) +{ + static const QRegExp entryPattern(QLatin1String( + "^(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\d{8}).(\\d{6})\\s+(.+)$") ); + + switch (m_status) { + case Header: + m_status = Entry; + break; + case Entry: + if (entryPattern.indexIn(line) != -1) { + ArchiveEntry e; + e[Permissions] = entryPattern.cap(1); + + // #280354: infozip may not show the right attributes for a given directory, so an entry + // ending with '/' is actually more reliable than 'd' bein in the attributes. + e[IsDirectory] = entryPattern.cap(10).endsWith(QLatin1Char('/')); + + e[Size] = entryPattern.cap(4).toInt(); + QString status = entryPattern.cap(5); + if (status[0].isUpper()) { + e[IsPasswordProtected] = true; + } + e[CompressedSize] = entryPattern.cap(6).toInt(); + + const QDateTime ts(QDate::fromString(entryPattern.cap(8), QLatin1String( "yyyyMMdd" )), + QTime::fromString(entryPattern.cap(9), QLatin1String( "hhmmss" ))); + e[Timestamp] = ts; + + e[FileName] = e[InternalID] = entryPattern.cap(10); + emit entry(e); + } + break; + } + + return true; +} + +KERFUFFLE_EXPORT_PLUGIN(CliPlugin) + diff --git a/ark/plugins/clizipplugin/cliplugin.h b/ark/plugins/clizipplugin/cliplugin.h new file mode 100644 index 00000000..3a310e0a --- /dev/null +++ b/ark/plugins/clizipplugin/cliplugin.h @@ -0,0 +1,47 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2011 Raphael Kubo da Costa + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef CLIPLUGIN_H +#define CLIPLUGIN_H + +#include "kerfuffle/cliinterface.h" + +class CliPlugin : public Kerfuffle::CliInterface +{ + Q_OBJECT + +public: + explicit CliPlugin(QObject *parent, const QVariantList &args); + virtual ~CliPlugin(); + + virtual QString escapeFileName(const QString &fileName) const; + + virtual Kerfuffle::ParameterList parameterList() const; + + virtual bool readListLine(const QString &line); + +private: + enum { + Header = 0, + Entry + } m_status; +}; + +#endif // CLIPLUGIN_H diff --git a/ark/plugins/clizipplugin/kerfuffle_clizip.desktop.cmake b/ark/plugins/clizipplugin/kerfuffle_clizip.desktop.cmake new file mode 100644 index 00000000..32ceced7 --- /dev/null +++ b/ark/plugins/clizipplugin/kerfuffle_clizip.desktop.cmake @@ -0,0 +1,69 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=Kerfuffle/Plugin +X-KDE-Library=kerfuffle_clizip +X-KDE-PluginInfo-Author=Harald Hvaal +X-KDE-PluginInfo-Email=haraldhv@stud.ntnu.no +X-KDE-PluginInfo-Name=kerfuffle_clizip +X-KDE-PluginInfo-Version=0.0.1 +X-KDE-PluginInfo-Website=http://www.kde.org +X-KDE-PluginInfo-License=GPLv2+ +X-KDE-Priority=160 +X-KDE-Kerfuffle-APIRevision=1 +X-KDE-Kerfuffle-ReadWrite=true +Name=ZIP archive plugin +Name[ar]=ملحق أرشيف ZIP +Name[ast]=Complementu d'archivu comprimíu ZIP +Name[bg]=Приставка за архиви ZIP +Name[bs]=Priključak ZIP arhiva +Name[ca]=Connector per arxius ZIP +Name[ca@valencia]=Connector per arxius ZIP +Name[cs]=Modul pro archiv ZIP +Name[da]=Plugin til ZIP-arkiver +Name[de]=ZIP-Archiv-Modul +Name[el]=Πρόσθετο αρχειοθήκης ZIP +Name[en_GB]=ZIP archive plugin +Name[es]=Complemento de archivo comprimido ZIP +Name[et]=ZIP-arhiivi plugin +Name[eu]=ZIP artxiboen plugina +Name[fi]=Zip-pakkaustuki +Name[fr]=Module externe d'archive « ZIP » +Name[ga]=Breiseán cartlainne ZIP +Name[gl]=Extensión de arquivo ZIP +Name[hr]=Arhivni priključak ZIP +Name[hu]=ZIP modul +Name[ia]=Plugin de archivar zip +Name[id]=Pengaya arsip ZIP +Name[it]=estensione per archivi ZIP +Name[ja]=ZIP アーカイブ用プラグイン +Name[kk]=ZIP архив плагині +Name[km]=កម្មវិធី​ជំនួយ​ប័ណ្ណសារ ZIP +Name[ko]=ZIP 압축 플러그인 +Name[lt]=ZIP archyvo priedas +Name[lv]=ZIP arhīvu spraudnis +Name[mr]=ZIP संग्रह प्लगइन +Name[nb]=Programtillegg for ZIP-arkiv +Name[nds]=Zip-Archievmoduul +Name[nl]=ZIP-archiefplug-in +Name[nn]=ZIP-arkivtillegg +Name[pa]=ਜ਼ਿੱਪ ਅਕਾਇਵ ਪਲੱਗਇਨ +Name[pl]=Wtyczka archiwów ZIP +Name[pt]='Plugin' de pacotes ZIP +Name[pt_BR]=Plugin de arquivos ZIP +Name[ro]=Modul de arhivă ZIP +Name[ru]=Поддержка архивов ZIP +Name[sk]=Modul ZIP archívu +Name[sl]=Vstavek za arhive ZIP +Name[sq]=ZIP arkiv plugin +Name[sr]=Прикључак ЗИП архива +Name[sr@ijekavian]=Прикључак ЗИП архива +Name[sr@ijekavianlatin]=Priključak ZIP arhiva +Name[sr@latin]=Priključak ZIP arhiva +Name[sv]=Insticksprogram för ZIP-arkiv +Name[th]=ส่วนเสริมการจัดการแฟ้มจัดเก็บบีบอัดแบบ ZIP +Name[tr]=ZIP arşivi eklentisi +Name[uk]=Додаток для архівів ZIP +Name[x-test]=xxZIP archive pluginxx +Name[zh_CN]=ZIP 归档插件 +Name[zh_TW]=ZIP 壓縮檔外掛程式 +MimeType=@SUPPORTED_CLIZIP_MIMETYPES@ diff --git a/ark/plugins/karchiveplugin/CMakeLists.txt b/ark/plugins/karchiveplugin/CMakeLists.txt new file mode 100644 index 00000000..8ef4382e --- /dev/null +++ b/ark/plugins/karchiveplugin/CMakeLists.txt @@ -0,0 +1,21 @@ +########### next target ############### + +set(SUPPORTED_KARCHIVE_MIMETYPES "application/zip;application/x-zip-compressed;application/x-tar;application/x-compressed-tar;application/x-bzip-compressed-tar;") + +set(kerfuffle_karchive_SRCS karchiveplugin.cpp) + +kde4_add_plugin(kerfuffle_karchive ${kerfuffle_karchive_SRCS}) + +target_link_libraries(kerfuffle_karchive ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS} kerfuffle ) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_karchive.desktop.cmake + ${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_karchive.desktop +) + +########### install files ############### + +install(TARGETS kerfuffle_karchive DESTINATION ${PLUGIN_INSTALL_DIR} ) +install( FILES kerfuffle_karchive.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + +set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}${SUPPORTED_KARCHIVE_MIMETYPES}" PARENT_SCOPE) diff --git a/ark/plugins/karchiveplugin/karchiveplugin.cpp b/ark/plugins/karchiveplugin/karchiveplugin.cpp new file mode 100644 index 00000000..31ce1d89 --- /dev/null +++ b/ark/plugins/karchiveplugin/karchiveplugin.cpp @@ -0,0 +1,331 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2007 Henrique Pinto + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "karchiveplugin.h" +#include "kerfuffle/queries.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +KArchiveInterface::KArchiveInterface(QObject *parent, const QVariantList &args) + : ReadWriteArchiveInterface(parent, args), m_archive(0) +{ + kDebug(); +} + +KArchiveInterface::~KArchiveInterface() +{ + delete m_archive; + m_archive = 0; +} + +KArchive *KArchiveInterface::archive() +{ + if (m_archive == 0) { + KMimeType::Ptr mimeType = KMimeType::findByPath(filename()); + + if (mimeType->is(QLatin1String("application/zip"))) { + m_archive = new KZip(filename()); + } else { + m_archive = new KTar(filename()); + } + + } + return m_archive; +} + +bool KArchiveInterface::list() +{ + kDebug(); + if (!archive()->isOpen() && !archive()->open(QIODevice::ReadOnly)) { + emit error(i18nc("@info", "Could not open the archive %1 for reading", filename())); + return false; + } else { + return browseArchive(archive()); + } +} + +void KArchiveInterface::getAllEntries(const KArchiveDirectory *dir, const QString &prefix, QList< QVariant > &list) +{ + foreach(const QString &entryName, dir->entries()) { + const KArchiveEntry *entry = dir->entry(entryName); + if (entry->isDirectory()) { + QString newPrefix = (prefix.isEmpty() ? prefix : prefix + QLatin1Char('/')) + entryName; + getAllEntries(static_cast(entry), newPrefix, list); + } + else { + list.append(prefix + QLatin1Char('/') + entryName); + } + } +} + +bool KArchiveInterface::copyFiles(const QList &files, const QString &destinationDirectory, ExtractionOptions options) +{ + const bool preservePaths = options.value(QLatin1String("PreservePaths")).toBool(); + const KArchiveDirectory *dir = archive()->directory(); + + if (!archive()->isOpen() && !archive()->open(QIODevice::ReadOnly)) { + emit error(i18nc("@info", "Could not open the archive %1 for reading", filename())); + return false; + } + + QList extrFiles = files; + if (extrFiles.isEmpty()) { // All files should be extracted + getAllEntries(dir, QString(), extrFiles); + } + + bool overwriteAllSelected = false; + bool autoSkipSelected = false; + QSet dirCache; + foreach(const QVariant &file, extrFiles) { + QString realDestination = destinationDirectory; + const KArchiveEntry *archiveEntry = dir->entry(file.toString()); + if (!archiveEntry) { + emit error(i18nc("@info", "File %1 not found in the archive" , file.toString())); + return false; + } + + if (preservePaths) { + QFileInfo fi(file.toString()); + QDir dest(destinationDirectory); + QString filepath = archiveEntry->isDirectory() ? fi.filePath() : fi.path(); + if (!dirCache.contains(filepath)) { + if (!dest.mkpath(filepath)) { + emit error(i18nc("@info", "Error creating directory %1", filepath)); + return false; + } + dirCache << filepath; + } + realDestination = dest.absolutePath() + QLatin1Char('/') + filepath; + } + + // TODO: handle errors, copyTo fails silently + if (!archiveEntry->isDirectory()) { // We don't need to do anything about directories + if (QFile::exists(realDestination + QLatin1Char('/') + archiveEntry->name()) && !overwriteAllSelected) { + if (autoSkipSelected) { + continue; + } + + int response = handleFileExistsMessage(realDestination, archiveEntry->name()); + + if (response == OverwriteCancel) { + break; + } + if (response == OverwriteYes || response == OverwriteAll) { + static_cast(archiveEntry)->copyTo(realDestination); + if (response == OverwriteAll) { + overwriteAllSelected = true; + } + } + if (response == OverwriteAutoSkip) { + autoSkipSelected = true; + } + } + else { + static_cast(archiveEntry)->copyTo(realDestination); + } + } + } + + return true; +} + +int KArchiveInterface::handleFileExistsMessage(const QString &dir, const QString &fileName) +{ + Kerfuffle::OverwriteQuery query(dir + QLatin1Char('/') + fileName); + query.setNoRenameMode(true); + emit userQuery(&query); + query.waitForResponse(); + + if (query.responseOverwrite()) { + return OverwriteYes; + } else if (query.responseSkip()) { + return OverwriteSkip; + } else if (query.responseOverwriteAll()) { + return OverwriteAll; + } else if (query.responseAutoSkip()) { + return OverwriteAutoSkip; + } + + return OverwriteCancel; +} + +bool KArchiveInterface::browseArchive(KArchive *archive) +{ + return processDir(archive->directory()); +} + +bool KArchiveInterface::processDir(const KArchiveDirectory *dir, const QString & prefix) +{ + foreach(const QString& entryName, dir->entries()) { + const KArchiveEntry *entry = dir->entry(entryName); + createEntryFor(entry, prefix); + if (entry->isDirectory()) { + QString newPrefix = (prefix.isEmpty() ? prefix : prefix + QLatin1Char('/')) + entryName; + processDir(static_cast(entry), newPrefix); + } + } + return true; +} + +void KArchiveInterface::createEntryFor(const KArchiveEntry *aentry, const QString& prefix) +{ + ArchiveEntry e; + QString fileName = prefix.isEmpty() ? aentry->name() : prefix + QLatin1Char('/') + aentry->name(); + + if (aentry->isDirectory() && !fileName.endsWith(QLatin1Char('/'))) + fileName += QLatin1Char('/'); + + e[ FileName ] = fileName; + e[ InternalID ] = e[ FileName ]; + e[ Permissions ] = permissionsString(aentry->permissions()); + e[ Owner ] = aentry->user(); + e[ Group ] = aentry->group(); + e[ IsDirectory ] = aentry->isDirectory(); + e[ Timestamp ] = aentry->datetime(); + if (!aentry->symLinkTarget().isEmpty()) { + e[ Link ] = aentry->symLinkTarget(); + } + if (aentry->isFile()) { + e[ Size ] = static_cast(aentry)->size(); + } + else { + e[ Size ] = 0; + } + emit entry(e); +} + +bool KArchiveInterface::addFiles(const QStringList &files, const Kerfuffle::CompressionOptions &options) +{ + Q_UNUSED(options) + kDebug() << "Starting..."; +// delete m_archive; +// m_archive = 0; + if (archive()->isOpen()) { + archive()->close(); + } + if (!archive()->open(QIODevice::ReadWrite)) { + emit error(i18nc("@info", "Could not open the archive %1 for writing.", filename())); + return false; + } + + kDebug() << "Archive opened for writing..."; + kDebug() << "Will add " << files.count() << " files"; + foreach(const QString &path, files) { + kDebug() << "Adding " << path; + QFileInfo fi(path); + Q_ASSERT(fi.exists()); + + if (fi.isDir()) { + if (archive()->addLocalDirectory(path, fi.fileName())) { + const KArchiveEntry *entry = archive()->directory()->entry(fi.fileName()); + createEntryFor(entry, QString()); + processDir((KArchiveDirectory*) archive()->directory()->entry(fi.fileName()), fi.fileName()); + } else { + emit error(i18nc("@info", "Could not add the directory %1 to the archive", path)); + return false; + } + } else { + if (archive()->addLocalFile(path, fi.fileName())) { + const KArchiveEntry *entry = archive()->directory()->entry(fi.fileName()); + createEntryFor(entry, QString()); + } else { + emit error(i18nc("@info", "Could not add the file %1 to the archive.", path)); + return false; + } + } + } + kDebug() << "Closing the archive"; + archive()->close(); + kDebug() << "Done"; + return true; +} + +bool KArchiveInterface::deleteFiles(const QList & files) +{ + Q_UNUSED(files) + return false; +} + +// Borrowed and adapted from KFileItemPrivate::parsePermissions. +QString KArchiveInterface::permissionsString(mode_t perm) +{ + static char buffer[ 12 ]; + + char uxbit,gxbit,oxbit; + + if ( (perm & (S_IXUSR|S_ISUID)) == (S_IXUSR|S_ISUID) ) + uxbit = 's'; + else if ( (perm & (S_IXUSR|S_ISUID)) == S_ISUID ) + uxbit = 'S'; + else if ( (perm & (S_IXUSR|S_ISUID)) == S_IXUSR ) + uxbit = 'x'; + else + uxbit = '-'; + + if ( (perm & (S_IXGRP|S_ISGID)) == (S_IXGRP|S_ISGID) ) + gxbit = 's'; + else if ( (perm & (S_IXGRP|S_ISGID)) == S_ISGID ) + gxbit = 'S'; + else if ( (perm & (S_IXGRP|S_ISGID)) == S_IXGRP ) + gxbit = 'x'; + else + gxbit = '-'; + + if ( (perm & (S_IXOTH|S_ISVTX)) == (S_IXOTH|S_ISVTX) ) + oxbit = 't'; + else if ( (perm & (S_IXOTH|S_ISVTX)) == S_ISVTX ) + oxbit = 'T'; + else if ( (perm & (S_IXOTH|S_ISVTX)) == S_IXOTH ) + oxbit = 'x'; + else + oxbit = '-'; + + // Include the type in the first char like kde3 did; people are more used to seeing it, + // even though it's not really part of the permissions per se. + if (S_ISDIR(perm)) + buffer[0] = 'd'; + else if (S_ISLNK(perm)) + buffer[0] = 'l'; + else + buffer[0] = '-'; + + buffer[1] = ((( perm & S_IRUSR ) == S_IRUSR ) ? 'r' : '-' ); + buffer[2] = ((( perm & S_IWUSR ) == S_IWUSR ) ? 'w' : '-' ); + buffer[3] = uxbit; + buffer[4] = ((( perm & S_IRGRP ) == S_IRGRP ) ? 'r' : '-' ); + buffer[5] = ((( perm & S_IWGRP ) == S_IWGRP ) ? 'w' : '-' ); + buffer[6] = gxbit; + buffer[7] = ((( perm & S_IROTH ) == S_IROTH ) ? 'r' : '-' ); + buffer[8] = ((( perm & S_IWOTH ) == S_IWOTH ) ? 'w' : '-' ); + buffer[9] = oxbit; + buffer[10] = 0; + + return QString::fromLatin1(buffer); +} + +KERFUFFLE_EXPORT_PLUGIN(KArchiveInterface) diff --git a/ark/plugins/karchiveplugin/karchiveplugin.h b/ark/plugins/karchiveplugin/karchiveplugin.h new file mode 100644 index 00000000..db7a6f51 --- /dev/null +++ b/ark/plugins/karchiveplugin/karchiveplugin.h @@ -0,0 +1,70 @@ +/* + * ark -- archiver for the KDE project + * + * Copyright (C) 2007 Henrique Pinto + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef KARCHIVEPLUGIN_H +#define KARCHIVEPLUGIN_H +#include "kerfuffle/archiveinterface.h" + +using namespace Kerfuffle; + +class KArchive; +class KArchiveEntry; +class KArchiveDirectory; + +class KArchiveInterface: public ReadWriteArchiveInterface +{ + Q_OBJECT +public: + explicit KArchiveInterface(QObject *parent = 0, const QVariantList &args = QVariantList()); + ~KArchiveInterface(); + + bool list(); + bool copyFiles(const QList &files, const QString &destinationDirectory, ExtractionOptions options); + + bool addFiles(const QStringList &files, const CompressionOptions &options); + bool deleteFiles(const QList & files); + +private: + enum { + OverwriteYes, + OverwriteSkip, + OverwriteAll, + OverwriteAutoSkip, + OverwriteCancel + }; + + bool browseArchive(KArchive *archive); + + bool processDir(const KArchiveDirectory *dir, const QString & prefix = QString()); + + void createEntryFor(const KArchiveEntry *aentry, const QString& prefix); + + QString permissionsString(mode_t perm); + + void getAllEntries(const KArchiveDirectory *dir, const QString &prefix, QList< QVariant > &list); + + int handleFileExistsMessage(const QString &dir, const QString &fileName); + + KArchive *archive(); + + KArchive *m_archive; +}; + +#endif // KARCHIVEPLUGIN_H diff --git a/ark/plugins/karchiveplugin/kerfuffle_karchive.desktop.cmake b/ark/plugins/karchiveplugin/kerfuffle_karchive.desktop.cmake new file mode 100644 index 00000000..84383585 --- /dev/null +++ b/ark/plugins/karchiveplugin/kerfuffle_karchive.desktop.cmake @@ -0,0 +1,129 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=Kerfuffle/Plugin +X-KDE-Library=kerfuffle_karchive +X-KDE-PluginInfo-Author=Henrique Pinto +X-KDE-PluginInfo-Email=henrique.pinto@kdemail.net +X-KDE-PluginInfo-Name=kerfuffle_karchive +X-KDE-PluginInfo-Version=0.0.1 +X-KDE-PluginInfo-Website=http://www.kde.org +X-KDE-PluginInfo-License=GPLv2+ +X-KDE-Priority=117 +X-KDE-Kerfuffle-APIRevision=1 +Name=kerfuffle_karchive +Name[ar]=kerfuffle_karchive +Name[ast]=kerfuffle_karchive +Name[bg]=kerfuffle_karchive +Name[bs]=kerfuffle_karchive +Name[ca]=kerfuffle_karchive +Name[ca@valencia]=kerfuffle_karchive +Name[cs]=kerfuffle_karchive +Name[da]=kerfuffle_karchive +Name[de]=kerfuffle_karchive +Name[el]=kerfuffle_karchive +Name[en_GB]=kerfuffle_karchive +Name[es]=kerfuffle_karchive +Name[et]=kerfuffle_karchive +Name[eu]=kerfuffle_karchive +Name[fi]=kerfuffle_karchive +Name[fr]=kerfuffle_karchive +Name[ga]=kerfuffle_karchive +Name[gl]=kerfuffle_karchive +Name[he]=kerfuffle_karchive +Name[hne]=करफुफल_केआर्काइव +Name[hr]=kerfuffle_karchive +Name[hu]=kerfuffle_karchive +Name[ia]=kerfuffle_karchive +Name[id]=kerfuffle_karchive +Name[it]=kerfuffle_karchive +Name[ja]=kerfuffle_karchive +Name[kk]=kerfuffle_karchive +Name[km]=kerfuffle_karchive +Name[ko]=kerfuffle_karchive +Name[lt]=kerfuffle_karchive +Name[lv]=kerfuffle_karchive +Name[mr]=कर्फफल_के-संग्रह +Name[nb]=kerfuffle_karchive +Name[nds]=kerfuffle_karchive +Name[nl]=kerfuffle_karchive +Name[nn]=kerfuffle_karchive +Name[pa]=kerfuffle_karchive +Name[pl]=kerfuffle_karchive +Name[pt]=kerfuffle_karchive +Name[pt_BR]=kerfuffle_karchive +Name[ro]=kerfuffle_karchive +Name[ru]=kerfuffle_karchive +Name[sk]=kerfuffle_karchive +Name[sl]=kerfuffle_karchive +Name[sq]=kerfuffle_karchive +Name[sr]=kerfuffle_karchive +Name[sr@ijekavian]=kerfuffle_karchive +Name[sr@ijekavianlatin]=kerfuffle_karchive +Name[sr@latin]=kerfuffle_karchive +Name[sv]=Kerfuffle Karchive +Name[ta]=கெர்ஃபஃபில் கேஆர்கைவ் +Name[th]=kerfuffle_karchive +Name[tr]=kerfuffle_karchive +Name[uk]=kerfuffle_karchive +Name[wa]=kerfuffle_karchive +Name[x-test]=xxkerfuffle_karchivexx +Name[zh_CN]=kerfuffle_karchive +Name[zh_TW]=kerfuffle_karchive +Comment=KArchive plugin for Kerfuffle +Comment[ar]=KArchive ملحق لـ Kerfuffle +Comment[ast]=Aplicación KArchive pa Kerfuffle +Comment[bg]=Приставка KArchive за Kerfuffle +Comment[bs]=Priključak KArchive za Kerfafl +Comment[ca]=Connector del KArchive pel Kerfuffle +Comment[ca@valencia]=Connector del KArchive pel Kerfuffle +Comment[cs]=KArchive modul pro Kerfuffle +Comment[da]=KArchive-plugin til Kerfuffle +Comment[de]=KArchive-Modul für Kerfuffle +Comment[el]=πρόσθετο KArchive για τη Kerfuffle +Comment[en_GB]=KArchive plugin for Kerfuffle +Comment[es]=Complemento KArchive para Kerfuffle +Comment[et]=Kerfuffle'i KArchive plugin +Comment[eu]=Kerfuffle-rentzako KArchive plugina +Comment[fi]=KArchive-pakkaustuki +Comment[fr]=Module externe « KArchive » pour Kerfuffle +Comment[ga]=Breiseán KArchive le haghaidh Kerfuffle +Comment[gl]=Extensión de KArchive para Kerfuffle +Comment[he]=תוסף KArchive עבור Kerfuffle +Comment[hne]=करफुफल बर केआर्काइव प्लगइन +Comment[hr]=Priključak KArchive za Kerfuffle +Comment[hu]=KArchive Kerfuffle-modul +Comment[ia]=Plugin de Karchive per Kerfuffle +Comment[id]=Pengaya KArchive untuk Kerfuffle +Comment[it]=Estensione KArchive per Kerfuffle +Comment[ja]=Kerfuffle のための KArchive プラグイン +Comment[kk]=Kerfuffle-ге арналған KArchive плагині +Comment[km]=កម្មវិធី​របស់ KArchive សម្រាប់ Kerfuffle +Comment[ko]=Kerfuffle을 위한 KArchive 플러그인 +Comment[lt]=KArchive Kerfuffle priedas +Comment[lv]=Kerfuffle KArchive spraudnis +Comment[mr]=कर्फफल करिता के-संग्रह प्लगइन +Comment[nb]=KArchive programtillegg for Kerfuffle +Comment[nds]=KArchive-Moduul för Kerfuffle +Comment[nl]=KArchive-plug-in voor Kerfuffle +Comment[nn]=KArchive-programtillegg til Kerfuffle +Comment[pl]=Wtyczka KArchive dla Kerfuffle +Comment[pt]='Plugin' do KArchive para o Kerfuffle +Comment[pt_BR]=Plugin KArchive para a Kerfuffle +Comment[ro]=Modul KArchive pentru Kerfuffle +Comment[ru]=Модуль KArchive для Kerfuffle +Comment[sk]=KArchive modul pre Kerfuffle +Comment[sl]=Vstavek KArchive za Kerfuffle +Comment[sq]=KArchive plugin për Kerfuffle +Comment[sr]=Прикључак KArchive за Керфафл +Comment[sr@ijekavian]=Прикључак KArchive за Керфафл +Comment[sr@ijekavianlatin]=Priključak KArchive za Kerfuffle +Comment[sr@latin]=Priključak KArchive za Kerfuffle +Comment[sv]=Karchive-insticksprogram för Kerfuffle +Comment[ta]=கெர்ஃபஃபில் க்கு கேஆர்கைவ் சொருகி +Comment[th]=ส่วนเสริม Kerfuffle สำหรับใช้งานร่วมกับ KArchive +Comment[tr]=Kerfuffle için KArchive eklentisi +Comment[uk]=Додаток KArchive для Kerfuffle +Comment[x-test]=xxKArchive plugin for Kerfufflexx +Comment[zh_CN]=Kerfuffle 的 KArchive 插件 +Comment[zh_TW]=Kerfuffle 的 KArchive 外掛程式 +MimeType=@SUPPORTED_KARCHIVE_MIMETYPES@ diff --git a/ark/plugins/libarchive/CMakeLists.txt b/ark/plugins/libarchive/CMakeLists.txt new file mode 100644 index 00000000..5d6d0096 --- /dev/null +++ b/ark/plugins/libarchive/CMakeLists.txt @@ -0,0 +1,41 @@ +include_directories(${LIBARCHIVE_INCLUDE_DIR}) + +########### next target ############### +set(SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES "application/x-deb;application/x-cd-image;application/x-bcpio;application/x-cpio;application/x-cpio-compressed;application/x-sv4cpio;application/x-sv4crc;") +set(SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES "application/x-tar;application/x-compressed-tar;application/x-bzip-compressed-tar;application/x-tarz;application/x-xz-compressed-tar;application/x-lzma-compressed-tar;") +if(HAVE_LIBARCHIVE_RPM_SUPPORT) + set(SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES "${SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES}application/x-rpm;application/x-source-rpm;") +endif(HAVE_LIBARCHIVE_RPM_SUPPORT) +if(HAVE_LIBARCHIVE_CAB_SUPPORT) + set(SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES "${SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES}application/vnd.ms-cab-compressed;") +endif(HAVE_LIBARCHIVE_CAB_SUPPORT) + +# This MIME type was originally set in ark.desktop but is not mentioned anywhere else. +# Assuming that, if it were supported, it would be here. +set(SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES "${SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES}application/x-servicepack;") + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_libarchive_readonly.desktop.cmake + ${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_libarchive_readonly.desktop +) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/kerfuffle_libarchive.desktop.cmake + ${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_libarchive.desktop +) + +set(kerfuffle_libarchive_SRCS libarchivehandler.cpp) + +kde4_add_plugin(kerfuffle_libarchive ${kerfuffle_libarchive_SRCS}) + +target_link_libraries(kerfuffle_libarchive ${KDE4_KIO_LIBS} ${KDE4_KDECORE_LIBS} ${LIBARCHIVE_LIBRARY} kerfuffle ) + +install(TARGETS kerfuffle_libarchive DESTINATION ${PLUGIN_INSTALL_DIR} ) + + +########### install files ############### + +install( FILES ${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_libarchive.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES ${CMAKE_CURRENT_BINARY_DIR}/kerfuffle_libarchive_readonly.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + +set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}${SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES}${SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES}" PARENT_SCOPE) diff --git a/ark/plugins/libarchive/kerfuffle_libarchive.desktop.cmake b/ark/plugins/libarchive/kerfuffle_libarchive.desktop.cmake new file mode 100644 index 00000000..5a943810 --- /dev/null +++ b/ark/plugins/libarchive/kerfuffle_libarchive.desktop.cmake @@ -0,0 +1,130 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=Kerfuffle/Plugin +X-KDE-Library=kerfuffle_libarchive +X-KDE-PluginInfo-Author=Henrique Pinto +X-KDE-PluginInfo-Email=henrique.pinto@kdemail.net +X-KDE-PluginInfo-Name=kerfuffle_libarchive +X-KDE-PluginInfo-Version=0.0.1 +X-KDE-PluginInfo-Website=http://www.kde.org +X-KDE-PluginInfo-License=BSD +X-KDE-Priority=100 +X-KDE-Kerfuffle-APIRevision=1 +X-KDE-Kerfuffle-ReadWrite=true +Name=kerfuffle_libarchive +Name[ar]=kerfuffle_libarchive +Name[ast]=kerfuffle_libarchive +Name[bg]=kerfuffle_libarchive +Name[bs]=kerfuffle_libarchive +Name[ca]=kerfuffle_libarchive +Name[ca@valencia]=kerfuffle_libarchive +Name[cs]=kerfuffle_libarchive +Name[da]=kerfuffle_libarchive +Name[de]=kerfuffle_libarchive +Name[el]=kerfuffle_libarchive +Name[en_GB]=kerfuffle_libarchive +Name[es]=kerfuffle_libarchive +Name[et]=kerfuffle_libarchive +Name[eu]=kerfuffle_libarchive +Name[fi]=kerfuffle_libarchive +Name[fr]=kerfuffle_libarchive +Name[ga]=kerfuffle_libarchive +Name[gl]=kerfuffle_libarchive +Name[he]=kerfuffle_libarchive +Name[hne]=करफुफल_लिबआर्काइव +Name[hr]=kerfuffle_libarchive +Name[hu]=kerfuffle_libarchive +Name[ia]=kerfuffle_libarchive +Name[id]=kerfuffle_libarchive +Name[it]=kerfuffle_libarchive +Name[ja]=kerfuffle_libarchive +Name[kk]=kerfuffle_libarchive +Name[km]=kerfuffle_libarchive +Name[ko]=kerfuffle_libarchive +Name[lt]=kerfuffle_libarchive +Name[lv]=kerfuffle_libarchive +Name[mr]=कर्फफल_लिब-संग्रह +Name[nb]=kerfuffle_libarchive +Name[nds]=kerfuffle_libarchive +Name[nl]=kerfuffle_libarchive +Name[nn]=kerfuffle_libarchive +Name[pa]=kerfuffle_libarchive +Name[pl]=kerfuffle_libarchive +Name[pt]=kerfuffle_libarchive +Name[pt_BR]=kerfuffle_libarchive +Name[ro]=kerfuffle_libarchive +Name[ru]=kerfuffle_libarchive +Name[sk]=kerfuffle_libarchive +Name[sl]=kerfuffle_libarchive +Name[sq]=kerfuffle_libarchive +Name[sr]=kerfuffle_libarchive +Name[sr@ijekavian]=kerfuffle_libarchive +Name[sr@ijekavianlatin]=kerfuffle_libarchive +Name[sr@latin]=kerfuffle_libarchive +Name[sv]=Kerfuffle Libarchive +Name[ta]=கெர்ஃபஃபில் லிப்ஆர்கைவ் +Name[th]=kerfuffle_libarchive +Name[tr]=kerfuffle_libarchive +Name[uk]=kerfuffle_libarchive +Name[wa]=kerfuffle_libarchive +Name[x-test]=xxkerfuffle_libarchivexx +Name[zh_CN]=kerfuffle_libarchive +Name[zh_TW]=kerfuffle_libarchive +Comment=LibArchive Plugin for Kerfuffle +Comment[ar]=LibArchive ملحق لـ Kerfuffle +Comment[ast]=Aplicación LibArchive pa Kerfuffle +Comment[bg]=Приставка LibArchive за Kerfuffle +Comment[bs]=Priključak libarchive za Kerfafl +Comment[ca]=Connector del LibArchive pel Kerfuffle +Comment[ca@valencia]=Connector del LibArchive pel Kerfuffle +Comment[cs]=LibArchive modul pro Kerfuffle +Comment[da]=LibArchive-plugin til Kerfuffle +Comment[de]=LibArchive-Modul für Kerfuffle +Comment[el]=πρόσθετο LibArchive για τη Kerfuffle +Comment[en_GB]=LibArchive Plugin for Kerfuffle +Comment[es]=Complemento LibArchive para Kerfuffle +Comment[et]=Kerfuffle'i LibArchive plugin +Comment[eu]=Kerfuffle-rentzako LibArchive plugina +Comment[fi]=LibArchive-pakkaustuki +Comment[fr]=Module externe « LibArchive » pour Kerfuffle +Comment[ga]=Breiseán LibArchive le haghaidh Kerfuffle +Comment[gl]=Extensión de libarchive para Kerfuffle +Comment[he]=תוסף LibArchive עבור Kerfuffle +Comment[hne]=करफुफल बर लिबआर्काइव प्लगइन +Comment[hr]=Priključak LibArchive za Kerfuffle +Comment[hu]=LibArchive Kerfuffle-modul +Comment[ia]=Plugin de Libarchive per Kerfuffle +Comment[id]=Pengaya LibArchive untuk Kerfuffle +Comment[it]=Estensione LibArchive per Kerfuffle +Comment[ja]=Kerfuffle のための LibArchive プラグイン +Comment[kk]=Kerfuffle-ге арналған LibArchive плагині +Comment[km]=កម្មវិធី​ជំនួយ LibArchive សម្រាប់ Kerfuffle +Comment[ko]=Kerfuffle을 위한 LibArchive 플러그인 +Comment[lt]=LibArchive Kerfuffle priedas +Comment[lv]=Kerfuffle LibArchive spraudnis +Comment[mr]=कर्फफल करिता लिब-संग्रह प्लगइन +Comment[nb]=LibArchive programtillegg for Kerfuffle +Comment[nds]=LibArchive-Moduul för Kerfuffle +Comment[nl]=LibArchive-plug-in voor Kerfuffle +Comment[nn]=LibArchive-programtillegg til Kerfuffle +Comment[pl]=Wtyczka libArchive dla Kerfuffle +Comment[pt]='Plugin' de LibArchive para o Kerfuffle +Comment[pt_BR]=Plugin LibArchive para a Kerfuffle +Comment[ro]=Modul LibArchive pentru Kerfuffle +Comment[ru]=Модуль LibArchive для Kerfuffle +Comment[sk]=LibArchive modul pre Kerfuffle +Comment[sl]=Vstavek LibArchive za Kerfuffle +Comment[sq]=LibArchive Plugin për Kerfuffle +Comment[sr]=Прикључак libarchive за Керфафл +Comment[sr@ijekavian]=Прикључак libarchive за Керфафл +Comment[sr@ijekavianlatin]=Priključak libarchive za Kerfuffle +Comment[sr@latin]=Priključak libarchive za Kerfuffle +Comment[sv]=Libarchive-insticksprogram för Kerfuffle +Comment[ta]=கெர்ஃபஃபில் க்கு லிப்ஆர்கைவ் சொருகி +Comment[th]=ส่วนเสริมของ Kerfuffle สำหรับใช้งานไลบรารี LibArchive +Comment[tr]=Kerfuffle için LibArchive eklentisi +Comment[uk]=Додаток LibArchive для Kerfuffle +Comment[x-test]=xxLibArchive Plugin for Kerfufflexx +Comment[zh_CN]=Kerfuffle 的 LibArchive 插件 +Comment[zh_TW]=Kerfuffle 的 LibArchive 外掛程式 +MimeType=@SUPPORTED_LIBARCHIVE_READWRITE_MIMETYPES@ diff --git a/ark/plugins/libarchive/kerfuffle_libarchive_readonly.desktop.cmake b/ark/plugins/libarchive/kerfuffle_libarchive_readonly.desktop.cmake new file mode 100644 index 00000000..254c4435 --- /dev/null +++ b/ark/plugins/libarchive/kerfuffle_libarchive_readonly.desktop.cmake @@ -0,0 +1,66 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=Kerfuffle/Plugin +X-KDE-Library=kerfuffle_libarchive +X-KDE-PluginInfo-Author=Henrique Pinto +X-KDE-PluginInfo-Email=henrique.pinto@kdemail.net +X-KDE-PluginInfo-Name=kerfuffle_libarchive +X-KDE-PluginInfo-Version=0.0.1 +X-KDE-PluginInfo-Website=http://www.kde.org +X-KDE-PluginInfo-License=BSD +X-KDE-Priority=100 +X-KDE-Kerfuffle-APIRevision=1 +X-KDE-Kerfuffle-ReadWrite=false +Name=kerfuffle_libarchive_readonly +Name[ar]=kerfuffle_libarchive_readonly +Name[bg]=kerfuffle_libarchive_readonly +Name[bs]=kerfuffle_libarchive_readonly +Name[ca]=kerfuffle_libarchive_readonly +Name[ca@valencia]=kerfuffle_libarchive_readonly +Name[cs]=kerfuffle_libarchive_readonly +Name[da]=kerfuffle_libarchive_readonly +Name[de]=kerfuffle_libarchive_readonly +Name[el]=kerfuffle_libarchive_readonly +Name[en_GB]=kerfuffle_libarchive_readonly +Name[es]=kerfuffle_libarchive_readonly +Name[et]=kerfuffle_libarchive_readonly +Name[eu]=kerfuffle_libarchive_readonly +Name[fi]=kerfuffle_libarchive_readonly +Name[fr]=kerfuffle_libarchive_readonly +Name[ga]=kerfuffle_libarchive_readonly +Name[gl]=kerfuffle_libarchive_readonly +Name[hr]=kerfuffle_libarchive_readonly +Name[hu]=kerfuffle_libarchive_readonly +Name[ia]=kerfuffle_libarchive_de_sol_lectura +Name[it]=kerfuffle_libarchive_readonly +Name[ja]=kerfuffle_libarchive_readonly +Name[kk]=kerfuffle_libarchive_readonly +Name[km]=kerfuffle_libarchive_readonly +Name[ko]=kerfuffle_libarchive_readonly +Name[lt]=kerfuffle_libarchive_readonly +Name[lv]=kerfuffle_libarchive_readonly +Name[mr]=कर्फफल_लिब-संग्रह_फक्त वाचण्यासाठी +Name[nb]=kerfuffle_libarchive_readonly +Name[nds]=kerfuffle_libarchive_readonly +Name[nl]=kerfuffle_libarchive_readonly +Name[pa]=kerfuffle_libarchive_readonly +Name[pl]=kerfuffle_libarchive_readonly +Name[pt]=kerfuffle_libarchive_readonly +Name[pt_BR]=kerfuffle_libarchive_readonly +Name[ro]=kerfuffle_libarchive_readonly +Name[ru]=kerfuffle_libarchive_readonly +Name[sk]=kerfuffle_libarchive_readonly +Name[sl]=kerfuffle_libarchive_readonly +Name[sr]=kerfuffle_libarchive_readonly +Name[sr@ijekavian]=kerfuffle_libarchive_readonly +Name[sr@ijekavianlatin]=kerfuffle_libarchive_readonly +Name[sr@latin]=kerfuffle_libarchive_readonly +Name[sv]=Kerfuffle Libarchive skrivskyddat +Name[th]=kerfuffle_libarchive_readonly +Name[tr]=kerfuffle_libarchive_readonly +Name[uk]=kerfuffle_libarchive_readonly +Name[wa]=kerfuffle_libarchive_readonly +Name[x-test]=xxkerfuffle_libarchive_readonlyxx +Name[zh_CN]=kerfuffle_libarchive_readonly +Name[zh_TW]=kerfuffle_libarchive_readonly +MimeType=@SUPPORTED_LIBARCHIVE_READONLY_MIMETYPES@ diff --git a/ark/plugins/libarchive/libarchivehandler.cpp b/ark/plugins/libarchive/libarchivehandler.cpp new file mode 100644 index 00000000..0fba5286 --- /dev/null +++ b/ark/plugins/libarchive/libarchivehandler.cpp @@ -0,0 +1,791 @@ +/* + * Copyright (c) 2007 Henrique Pinto + * Copyright (c) 2008-2009 Harald Hvaal + * Copyright (c) 2010 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "libarchivehandler.h" +#include "kerfuffle/kerfuffle_export.h" +#include "kerfuffle/queries.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct LibArchiveInterface::ArchiveReadCustomDeleter +{ + static inline void cleanup(struct archive *a) + { + if (a) { + archive_read_finish(a); + } + } +}; + +struct LibArchiveInterface::ArchiveWriteCustomDeleter +{ + static inline void cleanup(struct archive *a) + { + if (a) { + archive_write_finish(a); + } + } +}; + +LibArchiveInterface::LibArchiveInterface(QObject *parent, const QVariantList & args) + : ReadWriteArchiveInterface(parent, args) + , m_cachedArchiveEntryCount(0) + , m_emitNoEntries(false) + , m_extractedFilesSize(0) + , m_workDir(QDir::current()) + , m_archiveReadDisk(archive_read_disk_new()) + , m_abortOperation(false) +{ + archive_read_disk_set_standard_lookup(m_archiveReadDisk.data()); +} + +LibArchiveInterface::~LibArchiveInterface() +{ +} + +bool LibArchiveInterface::list() +{ + kDebug(); + + ArchiveRead arch_reader(archive_read_new()); + + if (!(arch_reader.data())) { + return false; + } + + if (archive_read_support_compression_all(arch_reader.data()) != ARCHIVE_OK) { + return false; + } + + if (archive_read_support_format_all(arch_reader.data()) != ARCHIVE_OK) { + return false; + } + + if (archive_read_open_filename(arch_reader.data(), QFile::encodeName(filename()), 10240) != ARCHIVE_OK) { + emit error(i18nc("@info", "Could not open the archive %1, libarchive cannot handle it.", + filename())); + return false; + } + + m_cachedArchiveEntryCount = 0; + m_extractedFilesSize = 0; + + struct archive_entry *aentry; + int result; + + while (!m_abortOperation && (result = archive_read_next_header(arch_reader.data(), &aentry)) == ARCHIVE_OK) { + if (!m_emitNoEntries) { + emitEntryFromArchiveEntry(aentry); + } + + m_extractedFilesSize += (qlonglong)archive_entry_size(aentry); + + m_cachedArchiveEntryCount++; + archive_read_data_skip(arch_reader.data()); + } + m_abortOperation = false; + + if (result != ARCHIVE_EOF) { + emit error(i18nc("@info", "The archive reading failed with the following error: %1", + QLatin1String( archive_error_string(arch_reader.data())))); + return false; + } + + return archive_read_close(arch_reader.data()) == ARCHIVE_OK; +} + +bool LibArchiveInterface::doKill() +{ + m_abortOperation = true; + return true; +} + +bool LibArchiveInterface::copyFiles(const QVariantList& files, const QString& destinationDirectory, ExtractionOptions options) +{ + kDebug() << "Changing current directory to " << destinationDirectory; + QDir::setCurrent(destinationDirectory); + + const bool extractAll = files.isEmpty(); + const bool preservePaths = options.value(QLatin1String( "PreservePaths" )).toBool(); + + QString rootNode = options.value(QLatin1String("RootNode"), QVariant()).toString(); + if ((!rootNode.isEmpty()) && (!rootNode.endsWith(QLatin1Char('/')))) { + rootNode.append(QLatin1Char('/')); + } + + ArchiveRead arch(archive_read_new()); + + if (!(arch.data())) { + return false; + } + + if (archive_read_support_compression_all(arch.data()) != ARCHIVE_OK) { + return false; + } + + if (archive_read_support_format_all(arch.data()) != ARCHIVE_OK) { + return false; + } + + if (archive_read_open_filename(arch.data(), QFile::encodeName(filename()), 10240) != ARCHIVE_OK) { + emit error(i18nc("@info", "Could not open the archive %1, libarchive cannot handle it.", + filename())); + return false; + } + + ArchiveWrite writer(archive_write_disk_new()); + if (!(writer.data())) { + return false; + } + + archive_write_disk_set_options(writer.data(), extractionFlags()); + + int entryNr = 0; + int totalCount = 0; + + if (extractAll) { + if (!m_cachedArchiveEntryCount) { + emit progress(0); + //TODO: once information progress has been implemented, send + //feedback here that the archive is being read + kDebug() << "For getting progress information, the archive will be listed once"; + m_emitNoEntries = true; + list(); + m_emitNoEntries = false; + } + totalCount = m_cachedArchiveEntryCount; + } else { + totalCount = files.size(); + } + + m_currentExtractedFilesSize = 0; + + bool overwriteAll = false; // Whether to overwrite all files + bool skipAll = false; // Whether to skip all files + struct archive_entry *entry; + + QString fileBeingRenamed; + + while (archive_read_next_header(arch.data(), &entry) == ARCHIVE_OK) { + fileBeingRenamed.clear(); + + // retry with renamed entry, fire an overwrite query again + // if the new entry also exists + retry: + const bool entryIsDir = S_ISDIR(archive_entry_mode(entry)); + + //we skip directories if not preserving paths + if (!preservePaths && entryIsDir) { + archive_read_data_skip(arch.data()); + continue; + } + + //entryName is the name inside the archive, full path + QString entryName = QDir::fromNativeSeparators(QFile::decodeName(archive_entry_pathname(entry))); + + if (entryName.startsWith(QLatin1Char( '/' ))) { + //for now we just can't handle absolute filenames in a tar archive. + //TODO: find out what to do here!! + emit error(i18n("This archive contains archive entries with absolute paths, which are not yet supported by ark.")); + + return false; + } + + if (files.contains(entryName) || entryName == fileBeingRenamed || extractAll) { + // entryFI is the fileinfo pointing to where the file will be + // written from the archive + QFileInfo entryFI(entryName); + //kDebug() << "setting path to " << archive_entry_pathname( entry ); + + const QString fileWithoutPath(entryFI.fileName()); + + //if we DON'T preserve paths, we cut the path and set the entryFI + //fileinfo to the one without the path + if (!preservePaths) { + //empty filenames (ie dirs) should have been skipped already, + //so asserting + Q_ASSERT(!fileWithoutPath.isEmpty()); + + archive_entry_copy_pathname(entry, QFile::encodeName(fileWithoutPath).constData()); + entryFI = QFileInfo(fileWithoutPath); + + //OR, if the commonBase has been set, then we remove this + //common base from the filename + } else if (!rootNode.isEmpty()) { + kDebug() << "Removing" << rootNode << "from" << entryName; + + const QString truncatedFilename(entryName.remove(0, rootNode.size())); + archive_entry_copy_pathname(entry, QFile::encodeName(truncatedFilename).constData()); + + entryFI = QFileInfo(truncatedFilename); + } + + //now check if the file about to be written already exists + if (!entryIsDir && entryFI.exists()) { + if (skipAll) { + archive_read_data_skip(arch.data()); + archive_entry_clear(entry); + continue; + } else if (!overwriteAll && !skipAll) { + Kerfuffle::OverwriteQuery query(entryName); + emit userQuery(&query); + query.waitForResponse(); + + if (query.responseCancelled()) { + archive_read_data_skip(arch.data()); + archive_entry_clear(entry); + break; + } else if (query.responseSkip()) { + archive_read_data_skip(arch.data()); + archive_entry_clear(entry); + continue; + } else if (query.responseAutoSkip()) { + archive_read_data_skip(arch.data()); + archive_entry_clear(entry); + skipAll = true; + continue; + } else if (query.responseRename()) { + const QString newName(query.newFilename()); + fileBeingRenamed = newName; + archive_entry_copy_pathname(entry, QFile::encodeName(newName).constData()); + goto retry; + } else if (query.responseOverwriteAll()) { + overwriteAll = true; + } + } + } + + //if there is an already existing directory: + if (entryIsDir && entryFI.exists()) { + if (entryFI.isWritable()) { + kDebug(1601) << "Warning, existing, but writable dir"; + } else { + kDebug(1601) << "Warning, existing, but non-writable dir. skipping"; + archive_entry_clear(entry); + archive_read_data_skip(arch.data()); + continue; + } + } + + int header_response; + kDebug() << "Writing " << fileWithoutPath << " to " << archive_entry_pathname(entry); + if ((header_response = archive_write_header(writer.data(), entry)) == ARCHIVE_OK) { + //if the whole archive is extracted and the total filesize is + //available, we use partial progress + copyData(arch.data(), writer.data(), (extractAll && m_extractedFilesSize)); + } else if (header_response == ARCHIVE_WARN) { + kDebug() << "Warning while writing " << entryName; + } else { + kDebug() << "Writing header failed with error code " << header_response + << "While attempting to write " << entryName; + } + + //if we only partially extract the archive and the number of + //archive entries is available we use a simple progress based on + //number of items extracted + if (!extractAll && m_cachedArchiveEntryCount) { + ++entryNr; + emit progress(float(entryNr) / totalCount); + } + archive_entry_clear(entry); + } else { + archive_read_data_skip(arch.data()); + } + } + + return archive_read_close(arch.data()) == ARCHIVE_OK; +} + +bool LibArchiveInterface::addFiles(const QStringList& files, const CompressionOptions& options) +{ + const bool creatingNewFile = !QFileInfo(filename()).exists(); + const QString tempFilename = filename() + QLatin1String( ".arkWriting" ); + const QString globalWorkDir = options.value(QLatin1String( "GlobalWorkDir" )).toString(); + + if (!globalWorkDir.isEmpty()) { + kDebug() << "GlobalWorkDir is set, changing dir to " << globalWorkDir; + m_workDir.setPath(globalWorkDir); + QDir::setCurrent(globalWorkDir); + } + + m_writtenFiles.clear(); + + ArchiveRead arch_reader; + if (!creatingNewFile) { + arch_reader.reset(archive_read_new()); + if (!(arch_reader.data())) { + emit error(i18n("The archive reader could not be initialized.")); + return false; + } + + if (archive_read_support_compression_all(arch_reader.data()) != ARCHIVE_OK) { + return false; + } + + if (archive_read_support_format_all(arch_reader.data()) != ARCHIVE_OK) { + return false; + } + + if (archive_read_open_filename(arch_reader.data(), QFile::encodeName(filename()), 10240) != ARCHIVE_OK) { + emit error(i18n("The source file could not be read.")); + return false; + } + } + + ArchiveWrite arch_writer(archive_write_new()); + if (!(arch_writer.data())) { + emit error(i18n("The archive writer could not be initialized.")); + return false; + } + + //pax_restricted is the libarchive default, let's go with that. + archive_write_set_format_pax_restricted(arch_writer.data()); + + int ret; + if (creatingNewFile) { + if (filename().right(2).toUpper() == QLatin1String( "GZ" )) { + kDebug() << "Detected gzip compression for new file"; + ret = archive_write_set_compression_gzip(arch_writer.data()); + } else if (filename().right(3).toUpper() == QLatin1String( "BZ2" )) { + kDebug() << "Detected bzip2 compression for new file"; + ret = archive_write_set_compression_bzip2(arch_writer.data()); +#ifdef HAVE_LIBARCHIVE_XZ_SUPPORT + } else if (filename().right(2).toUpper() == QLatin1String( "XZ" )) { + kDebug() << "Detected xz compression for new file"; + ret = archive_write_set_compression_xz(arch_writer.data()); +#endif +#ifdef HAVE_LIBARCHIVE_LZMA_SUPPORT + } else if (filename().right(4).toUpper() == QLatin1String( "LZMA" )) { + kDebug() << "Detected lzma compression for new file"; + ret = archive_write_set_compression_lzma(arch_writer.data()); +#endif + } else if (filename().right(3).toUpper() == QLatin1String( "TAR" )) { + kDebug() << "Detected no compression for new file (pure tar)"; + ret = archive_write_set_compression_none(arch_writer.data()); + } else { + kDebug() << "Falling back to gzip"; + ret = archive_write_set_compression_gzip(arch_writer.data()); + } + + if (ret != ARCHIVE_OK) { + emit error(i18nc("@info", "Setting the compression method failed with the following error: %1", + QLatin1String(archive_error_string(arch_writer.data())))); + + return false; + } + } else { + switch (archive_compression(arch_reader.data())) { + case ARCHIVE_COMPRESSION_GZIP: + ret = archive_write_set_compression_gzip(arch_writer.data()); + break; + case ARCHIVE_COMPRESSION_BZIP2: + ret = archive_write_set_compression_bzip2(arch_writer.data()); + break; +#ifdef HAVE_LIBARCHIVE_XZ_SUPPORT + case ARCHIVE_COMPRESSION_XZ: + ret = archive_write_set_compression_xz(arch_writer.data()); + break; +#endif +#ifdef HAVE_LIBARCHIVE_LZMA_SUPPORT + case ARCHIVE_COMPRESSION_LZMA: + ret = archive_write_set_compression_lzma(arch_writer.data()); + break; +#endif + case ARCHIVE_COMPRESSION_NONE: + ret = archive_write_set_compression_none(arch_writer.data()); + break; + default: + emit error(i18n("The compression type '%1' is not supported by Ark.", QLatin1String(archive_compression_name(arch_reader.data())))); + return false; + } + + if (ret != ARCHIVE_OK) { + emit error(i18nc("@info", "Setting the compression method failed with the following error: %1", QLatin1String(archive_error_string(arch_writer.data())))); + return false; + } + } + + ret = archive_write_open_filename(arch_writer.data(), QFile::encodeName(tempFilename)); + if (ret != ARCHIVE_OK) { + emit error(i18nc("@info", "Opening the archive for writing failed with the following error: %1", QLatin1String(archive_error_string(arch_writer.data())))); + return false; + } + + //**************** first write the new files + foreach(const QString& selectedFile, files) { + bool success; + + success = writeFile(selectedFile, arch_writer.data()); + + if (!success) { + QFile::remove(tempFilename); + return false; + } + + if (QFileInfo(selectedFile).isDir()) { + QDirIterator it(selectedFile, + QDir::AllEntries | QDir::Readable | + QDir::Hidden | QDir::NoDotAndDotDot, + QDirIterator::Subdirectories); + + while (it.hasNext()) { + const QString path = it.next(); + + if ((it.fileName() == QLatin1String("..")) || + (it.fileName() == QLatin1String("."))) { + continue; + } + + success = writeFile(path + + (it.fileInfo().isDir() ? QLatin1String( "/" ) : QLatin1String( "" )), + arch_writer.data()); + + if (!success) { + QFile::remove(tempFilename); + return false; + } + } + } + } + + struct archive_entry *entry; + + //and if we have old elements... + if (!creatingNewFile) { + //********** copy old elements from previous archive to new archive + while (archive_read_next_header(arch_reader.data(), &entry) == ARCHIVE_OK) { + if (m_writtenFiles.contains(QFile::decodeName(archive_entry_pathname(entry)))) { + archive_read_data_skip(arch_reader.data()); + kDebug() << "Entry already existing, will be refresh: ===> " << archive_entry_pathname(entry); + continue; + } + + int header_response; + //kDebug() << "Writing entry " << fn; + if ((header_response = archive_write_header(arch_writer.data(), entry)) == ARCHIVE_OK) { + //if the whole archive is extracted and the total filesize is + //available, we use partial progress + copyData(arch_reader.data(), arch_writer.data(), false); + } else { + kDebug() << "Writing header failed with error code " << header_response; + QFile::remove(tempFilename); + return false; + } + + archive_entry_clear(entry); + } + + //everything seems OK, so we remove the source file and replace it with + //the new one. + //TODO: do some extra checks to see if this is really OK + QFile::remove(filename()); + } + + QFile::rename(tempFilename, filename()); + + return true; +} + +bool LibArchiveInterface::deleteFiles(const QVariantList& files) +{ + const QString tempFilename = filename() + QLatin1String( ".arkWriting" ); + + ArchiveRead arch_reader(archive_read_new()); + if (!(arch_reader.data())) { + emit error(i18n("The archive reader could not be initialized.")); + return false; + } + + if (archive_read_support_compression_all(arch_reader.data()) != ARCHIVE_OK) { + return false; + } + + if (archive_read_support_format_all(arch_reader.data()) != ARCHIVE_OK) { + return false; + } + + if (archive_read_open_filename(arch_reader.data(), QFile::encodeName(filename()), 10240) != ARCHIVE_OK) { + emit error(i18n("The source file could not be read.")); + return false; + } + + ArchiveWrite arch_writer(archive_write_new()); + if (!(arch_writer.data())) { + emit error(i18n("The archive writer could not be initialized.")); + return false; + } + + //pax_restricted is the libarchive default, let's go with that. + archive_write_set_format_pax_restricted(arch_writer.data()); + + int ret; + switch (archive_compression(arch_reader.data())) { + case ARCHIVE_COMPRESSION_GZIP: + ret = archive_write_set_compression_gzip(arch_writer.data()); + break; + case ARCHIVE_COMPRESSION_BZIP2: + ret = archive_write_set_compression_bzip2(arch_writer.data()); + break; +#ifdef HAVE_LIBARCHIVE_XZ_SUPPORT + case ARCHIVE_COMPRESSION_XZ: + ret = archive_write_set_compression_xz(arch_writer.data()); + break; +#endif +#ifdef HAVE_LIBARCHIVE_LZMA_SUPPORT + case ARCHIVE_COMPRESSION_LZMA: + ret = archive_write_set_compression_lzma(arch_writer.data()); + break; +#endif + case ARCHIVE_COMPRESSION_NONE: + ret = archive_write_set_compression_none(arch_writer.data()); + break; + default: + emit error(i18n("The compression type '%1' is not supported by Ark.", QLatin1String(archive_compression_name(arch_reader.data())))); + return false; + } + + if (ret != ARCHIVE_OK) { + emit error(i18nc("@info", "Setting the compression method failed with the following error: %1", QLatin1String(archive_error_string(arch_writer.data())))); + return false; + } + + ret = archive_write_open_filename(arch_writer.data(), QFile::encodeName(tempFilename)); + if (ret != ARCHIVE_OK) { + emit error(i18nc("@info", "Opening the archive for writing failed with the following error: %1", QLatin1String(archive_error_string(arch_writer.data())))); + return false; + } + + struct archive_entry *entry; + + //********** copy old elements from previous archive to new archive + while (archive_read_next_header(arch_reader.data(), &entry) == ARCHIVE_OK) { + if (files.contains(QFile::decodeName(archive_entry_pathname(entry)))) { + archive_read_data_skip(arch_reader.data()); + kDebug() << "Entry to be deleted, skipping" + << archive_entry_pathname(entry); + emit entryRemoved(QFile::decodeName(archive_entry_pathname(entry))); + continue; + } + + int header_response; + //kDebug() << "Writing entry " << fn; + if ((header_response = archive_write_header(arch_writer.data(), entry)) == ARCHIVE_OK) { + //if the whole archive is extracted and the total filesize is + //available, we use partial progress + copyData(arch_reader.data(), arch_writer.data(), false); + } else { + kDebug() << "Writing header failed with error code " << header_response; + return false; + } + } + + //everything seems OK, so we remove the source file and replace it with + //the new one. + //TODO: do some extra checks to see if this is really OK + QFile::remove(filename()); + QFile::rename(tempFilename, filename()); + + return true; +} + +void LibArchiveInterface::emitEntryFromArchiveEntry(struct archive_entry *aentry) +{ + ArchiveEntry e; + +#ifdef _MSC_VER + e[FileName] = QDir::fromNativeSeparators(QString::fromUtf16((ushort*)archive_entry_pathname_w(aentry))); +#else + e[FileName] = QDir::fromNativeSeparators(QString::fromWCharArray(archive_entry_pathname_w(aentry))); +#endif + e[InternalID] = e[FileName]; + + const QString owner = QString::fromAscii(archive_entry_uname(aentry)); + if (!owner.isEmpty()) { + e[Owner] = owner; + } + + const QString group = QString::fromAscii(archive_entry_gname(aentry)); + if (!group.isEmpty()) { + e[Group] = group; + } + + e[Size] = (qlonglong)archive_entry_size(aentry); + e[IsDirectory] = S_ISDIR(archive_entry_mode(aentry)); + + if (archive_entry_symlink(aentry)) { + e[Link] = QLatin1String( archive_entry_symlink(aentry) ); + } + + e[Timestamp] = QDateTime::fromTime_t(archive_entry_mtime(aentry)); + + emit entry(e); +} + +int LibArchiveInterface::extractionFlags() const +{ + int result = ARCHIVE_EXTRACT_TIME; + result |= ARCHIVE_EXTRACT_SECURE_NODOTDOT; + + // TODO: Don't use arksettings here + /*if ( ArkSettings::preservePerms() ) + { + result &= ARCHIVE_EXTRACT_PERM; + } + + if ( !ArkSettings::extractOverwrite() ) + { + result &= ARCHIVE_EXTRACT_NO_OVERWRITE; + }*/ + + return result; +} + +void LibArchiveInterface::copyData(const QString& filename, struct archive *dest, bool partialprogress) +{ + char buff[10240]; + ssize_t readBytes; + QFile file(filename); + + if (!file.open(QIODevice::ReadOnly)) { + return; + } + + readBytes = file.read(buff, sizeof(buff)); + while (readBytes > 0) { + /* int writeBytes = */ + archive_write_data(dest, buff, readBytes); + if (archive_errno(dest) != ARCHIVE_OK) { + kDebug() << "Error while writing..." << archive_error_string(dest) << "(error nb =" << archive_errno(dest) << ')'; + return; + } + + if (partialprogress) { + m_currentExtractedFilesSize += readBytes; + emit progress(float(m_currentExtractedFilesSize) / m_extractedFilesSize); + } + + readBytes = file.read(buff, sizeof(buff)); + } + + file.close(); +} + +void LibArchiveInterface::copyData(struct archive *source, struct archive *dest, bool partialprogress) +{ + char buff[10240]; + ssize_t readBytes; + + readBytes = archive_read_data(source, buff, sizeof(buff)); + while (readBytes > 0) { + /* int writeBytes = */ + archive_write_data(dest, buff, readBytes); + if (archive_errno(dest) != ARCHIVE_OK) { + kDebug() << "Error while extracting..." << archive_error_string(dest) << "(error nb =" << archive_errno(dest) << ')'; + return; + } + + if (partialprogress) { + m_currentExtractedFilesSize += readBytes; + emit progress(float(m_currentExtractedFilesSize) / m_extractedFilesSize); + } + + readBytes = archive_read_data(source, buff, sizeof(buff)); + } +} + +// TODO: if we merge this with copyData(), we can pass more data +// such as an fd to archive_read_disk_entry_from_file() +bool LibArchiveInterface::writeFile(const QString& fileName, struct archive* arch_writer) +{ + int header_response; + + const bool trailingSlash = fileName.endsWith(QLatin1Char( '/' )); + + // #191821: workDir must be used instead of QDir::current() + // so that symlinks aren't resolved automatically + // TODO: this kind of call should be moved upwards in the + // class hierarchy to avoid code duplication + const QString relativeName = m_workDir.relativeFilePath(fileName) + (trailingSlash ? QLatin1String( "/" ) : QLatin1String( "" )); + + // #253059: Even if we use archive_read_disk_entry_from_file, + // libarchive may have been compiled without HAVE_LSTAT, + // or something may have caused it to follow symlinks, in + // which case stat() will be called. To avoid this, we + // call lstat() ourselves. + KDE_struct_stat st; + KDE_lstat(QFile::encodeName(fileName).constData(), &st); + + struct archive_entry *entry = archive_entry_new(); + archive_entry_set_pathname(entry, QFile::encodeName(relativeName).constData()); + archive_entry_copy_sourcepath(entry, QFile::encodeName(fileName).constData()); + archive_read_disk_entry_from_file(m_archiveReadDisk.data(), entry, -1, &st); + + kDebug() << "Writing new entry " << archive_entry_pathname(entry); + if ((header_response = archive_write_header(arch_writer, entry)) == ARCHIVE_OK) { + //if the whole archive is extracted and the total filesize is + //available, we use partial progress + copyData(fileName, arch_writer, false); + } else { + kDebug() << "Writing header failed with error code " << header_response; + kDebug() << "Error while writing..." << archive_error_string(arch_writer) << "(error nb =" << archive_errno(arch_writer) << ')'; + + emit error(i18nc("@info Error in a message box", + "Ark could not compress %1:%2", + fileName, + QLatin1String(archive_error_string(arch_writer)))); + + archive_entry_free(entry); + + return false; + } + + m_writtenFiles.push_back(relativeName); + + emitEntryFromArchiveEntry(entry); + + archive_entry_free(entry); + + return true; +} + +KERFUFFLE_EXPORT_PLUGIN(LibArchiveInterface) + +#include "libarchivehandler.moc" diff --git a/ark/plugins/libarchive/libarchivehandler.h b/ark/plugins/libarchive/libarchivehandler.h new file mode 100644 index 00000000..088c0fe8 --- /dev/null +++ b/ark/plugins/libarchive/libarchivehandler.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2007 Henrique Pinto + * Copyright (c) 2008-2009 Harald Hvaal + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBARCHIVEHANDLER_H +#define LIBARCHIVEHANDLER_H + +#include "kerfuffle/archiveinterface.h" + +#include +#include +#include +#include + +using namespace Kerfuffle; + +class LibArchiveInterface: public ReadWriteArchiveInterface +{ + Q_OBJECT + +public: + explicit LibArchiveInterface(QObject *parent, const QVariantList& args); + ~LibArchiveInterface(); + + bool list(); + bool doKill(); + bool copyFiles(const QVariantList& files, const QString& destinationDirectory, ExtractionOptions options); + bool addFiles(const QStringList& files, const CompressionOptions& options); + bool deleteFiles(const QVariantList& files); + +private: + void emitEntryFromArchiveEntry(struct archive_entry *entry); + int extractionFlags() const; + void copyData(const QString& filename, struct archive *dest, bool partialprogress = true); + void copyData(struct archive *source, struct archive *dest, bool partialprogress = true); + bool writeFile(const QString& fileName, struct archive* arch); + + struct ArchiveReadCustomDeleter; + struct ArchiveWriteCustomDeleter; + typedef QScopedPointer ArchiveRead; + typedef QScopedPointer ArchiveWrite; + + int m_cachedArchiveEntryCount; + qlonglong m_currentExtractedFilesSize; + bool m_emitNoEntries; + qlonglong m_extractedFilesSize; + QDir m_workDir; + QStringList m_writtenFiles; + ArchiveRead m_archiveReadDisk; + bool m_abortOperation; +}; + +#endif // LIBARCHIVEHANDLER_H diff --git a/ark/plugins/libsinglefileplugin/CMakeLists.txt b/ark/plugins/libsinglefileplugin/CMakeLists.txt new file mode 100644 index 00000000..93262971 --- /dev/null +++ b/ark/plugins/libsinglefileplugin/CMakeLists.txt @@ -0,0 +1,69 @@ +set(kerfuffle_singlefile_SRCS singlefileplugin.cpp) +set(SUPPORTED_LIBSINGLEFILE_MIMETYPES "") + +# This MIME type was originally set in ark.desktop but is does not +# seem to be supported anywhere. Assuming that, if it were supported, +# it would be here. +set(SUPPORTED_LIBSINGLEFILE_MIMETYPES "${SUPPORTED_LIBSINGLEFILE_MIMETYPES}application/x-compress;") + +# +# GZip files +# +macro_optional_find_package(ZLIB) + +macro_log_feature(ZLIB_FOUND "ZLib" "The Zlib compression library" "http://www.zlib.net" FALSE "" "Required for the .gz format support in Ark") + +if (ZLIB_FOUND) + set(kerfuffle_libgz_SRCS gzplugin.cpp ${kerfuffle_singlefile_SRCS}) + set(SUPPORTED_LIBSINGLEFILE_MIMETYPES "${SUPPORTED_LIBSINGLEFILE_MIMETYPES}application/x-gzip;") + + kde4_add_plugin(kerfuffle_libgz ${kerfuffle_libgz_SRCS}) + + target_link_libraries(kerfuffle_libgz ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS} kerfuffle ) + + install(TARGETS kerfuffle_libgz DESTINATION ${PLUGIN_INSTALL_DIR} ) + + install( FILES kerfuffle_libgz.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +endif (ZLIB_FOUND) + +# +# Bzip2 files +# +macro_optional_find_package(BZip2) + +macro_log_feature(BZIP2_FOUND "BZip2" "A high-quality data compressor" "http://www.bzip.org" FALSE "" "Required for the .bz2 format support in Ark") + +if (BZIP2_FOUND) + set(kerfuffle_libbz2_SRCS bz2plugin.cpp ${kerfuffle_singlefile_SRCS}) + set(SUPPORTED_LIBSINGLEFILE_MIMETYPES "${SUPPORTED_LIBSINGLEFILE_MIMETYPES}application/x-bzip;application/x-bzip2;") + + kde4_add_plugin(kerfuffle_libbz2 ${kerfuffle_libbz2_SRCS}) + + target_link_libraries(kerfuffle_libbz2 ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS} kerfuffle ) + + install(TARGETS kerfuffle_libbz2 DESTINATION ${PLUGIN_INSTALL_DIR} ) + + install( FILES kerfuffle_libbz2.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +endif (BZIP2_FOUND) + +# +# LZMA files +# +macro_optional_find_package(LibLZMA) + +macro_log_feature(LIBLZMA_FOUND "LibLZMA" "Liblzma is used for the .xz and .lzma formats" "http://tukaani.org/xz/" FALSE "" "Required for the .xz and .lzma format support in Ark") + +if (LIBLZMA_FOUND) + set(kerfuffle_libxz_SRCS xzplugin.cpp ${kerfuffle_singlefile_SRCS}) + set(SUPPORTED_LIBSINGLEFILE_MIMETYPES "${SUPPORTED_LIBSINGLEFILE_MIMETYPES}application/x-lzma;application/x-xz;") + + kde4_add_plugin(kerfuffle_libxz ${kerfuffle_libxz_SRCS}) + + target_link_libraries(kerfuffle_libxz ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS} kerfuffle ) + + install(TARGETS kerfuffle_libxz DESTINATION ${PLUGIN_INSTALL_DIR} ) + + install( FILES kerfuffle_libxz.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +endif (LIBLZMA_FOUND) + +set(SUPPORTED_ARK_MIMETYPES "${SUPPORTED_ARK_MIMETYPES}${SUPPORTED_LIBSINGLEFILE_MIMETYPES}" PARENT_SCOPE) diff --git a/ark/plugins/libsinglefileplugin/bz2plugin.cpp b/ark/plugins/libsinglefileplugin/bz2plugin.cpp new file mode 100644 index 00000000..de9c9b41 --- /dev/null +++ b/ark/plugins/libsinglefileplugin/bz2plugin.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2009 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "bz2plugin.h" +#include "kerfuffle/kerfuffle_export.h" + +#include + +LibBzip2Interface::LibBzip2Interface(QObject *parent, const QVariantList & args) + : LibSingleFileInterface(parent, args) +{ + m_mimeType = QLatin1String( "application/x-bzip" ); + m_possibleExtensions.append(QLatin1String( ".bz2" )); +} + +LibBzip2Interface::~LibBzip2Interface() +{ +} + +KERFUFFLE_EXPORT_PLUGIN(LibBzip2Interface) + +#include "bz2plugin.moc" diff --git a/ark/plugins/libsinglefileplugin/bz2plugin.h b/ark/plugins/libsinglefileplugin/bz2plugin.h new file mode 100644 index 00000000..94f7d3c1 --- /dev/null +++ b/ark/plugins/libsinglefileplugin/bz2plugin.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2009 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BZ2PLUGIN_H +#define BZ2PLUGIN_H + +#include "singlefileplugin.h" + +class LibBzip2Interface : public LibSingleFileInterface +{ + Q_OBJECT + +public: + LibBzip2Interface(QObject *parent, const QVariantList & args); + virtual ~LibBzip2Interface(); +}; + +#endif // BZ2PLUGIN_H diff --git a/ark/plugins/libsinglefileplugin/gzplugin.cpp b/ark/plugins/libsinglefileplugin/gzplugin.cpp new file mode 100644 index 00000000..469432a7 --- /dev/null +++ b/ark/plugins/libsinglefileplugin/gzplugin.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2009 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "gzplugin.h" +#include "kerfuffle/kerfuffle_export.h" + +#include + +LibGzipInterface::LibGzipInterface(QObject *parent, const QVariantList & args) + : LibSingleFileInterface(parent, args) +{ + m_mimeType = QLatin1String( "application/x-gzip" ); + m_possibleExtensions.append(QLatin1String( ".gz" )); +} + +LibGzipInterface::~LibGzipInterface() +{ +} + +KERFUFFLE_EXPORT_PLUGIN(LibGzipInterface) + +#include "gzplugin.moc" diff --git a/ark/plugins/libsinglefileplugin/gzplugin.h b/ark/plugins/libsinglefileplugin/gzplugin.h new file mode 100644 index 00000000..7ad5ef79 --- /dev/null +++ b/ark/plugins/libsinglefileplugin/gzplugin.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2009 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GZPLUGIN_H +#define GZPLUGIN_H + +#include "singlefileplugin.h" + +class LibGzipInterface : public LibSingleFileInterface +{ + Q_OBJECT + +public: + LibGzipInterface(QObject *parent, const QVariantList & args); + virtual ~LibGzipInterface(); +}; + +#endif // GZPLUGIN_H diff --git a/ark/plugins/libsinglefileplugin/kerfuffle_libbz2.desktop b/ark/plugins/libsinglefileplugin/kerfuffle_libbz2.desktop new file mode 100644 index 00000000..173f37d4 --- /dev/null +++ b/ark/plugins/libsinglefileplugin/kerfuffle_libbz2.desktop @@ -0,0 +1,124 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=Kerfuffle/Plugin +X-KDE-Library=kerfuffle_libbz2 +X-KDE-PluginInfo-Author=Raphael Kubo da Costa +X-KDE-PluginInfo-Email=rakuco@FreeBSD.org +X-KDE-PluginInfo-Name=kerfuffle_libbz2 +X-KDE-PluginInfo-Version=0.0.1 +X-KDE-PluginInfo-Website=http://www.kde.org +X-KDE-PluginInfo-License=BSD +X-KDE-Priority=100 +X-KDE-Kerfuffle-APIRevision=1 +#X-KDE-Kerfuffle-ReadWrite=true +Name=kerfuffle_libbz2 +Name[ar]=kerfuffle_libbz2 +Name[ast]=kerfuffle_libbz2 +Name[bg]=kerfuffle_libbz2 +Name[bs]=kerfuffle_libbz2 +Name[ca]=kerfuffle_libbz2 +Name[ca@valencia]=kerfuffle_libbz2 +Name[cs]=kerfuffle_libbz2 +Name[da]=kerfuffle_libbz2 +Name[de]=kerfuffle_libbz2 +Name[el]=kerfuffle_libbz2 +Name[en_GB]=kerfuffle_libbz2 +Name[es]=kerfuffle_libbz2 +Name[et]=kerfuffle_libbz2 +Name[eu]=kerfuffle_libbz2 +Name[fi]=kerfuffle_libbz2 +Name[fr]=kerfuffle_libbz2 +Name[ga]=kerfuffle_libbz2 +Name[gl]=kerfuffle_libbz2 +Name[hr]=kerfuffle_libbz2 +Name[hu]=kerfuffle_libbz2 +Name[ia]=kerfuffle_libbz2 +Name[id]=kerfuffle_libbz2 +Name[it]=kerfuffle_libbz2 +Name[ja]=kerfuffle_libbz2 +Name[kk]=kerfuffle_libbz2 +Name[km]=kerfuffle_libbz2 +Name[ko]=kerfuffle_libbz2 +Name[lt]=kerfuffle_libbz2 +Name[lv]=kerfuffle_libbz2 +Name[mr]=कर्फफल_लिब-बिझेड२ +Name[nb]=kerfuffle_libbz2 +Name[nds]=kerfuffle_libbz2 +Name[nl]=kerfuffle_libbz2 +Name[nn]=kerfuffle_libbz2 +Name[pa]=kerfuffle_libbz2 +Name[pl]=kerfuffle_libbz2 +Name[pt]=kerfuffle_libbz2 +Name[pt_BR]=kerfuffle_libbz2 +Name[ro]=kerfuffle_libbz2 +Name[ru]=kerfuffle_libbz2 +Name[sk]=kerfuffle_libbz2 +Name[sl]=kerfuffle_libbz2 +Name[sq]=kerfuffle_libbz2 +Name[sr]=kerfuffle_libbz2 +Name[sr@ijekavian]=kerfuffle_libbz2 +Name[sr@ijekavianlatin]=kerfuffle_libbz2 +Name[sr@latin]=kerfuffle_libbz2 +Name[sv]=Kerfuffle Libbz2 +Name[th]=kerfuffle_libbz2 +Name[tr]=kerfuffle_libbz2 +Name[uk]=kerfuffle_libbz2 +Name[wa]=kerfuffle_libbz2 +Name[x-test]=xxkerfuffle_libbz2xx +Name[zh_CN]=kerfuffle_libbz2 +Name[zh_TW]=kerfuffle_libbz2 +Comment=libbz2 plugin for Kerfuffle +Comment[ar]=ملحق libbz2 لـ Kerfuffle +Comment[ast]=Complementu libbz2 pa Kerfuffle +Comment[bg]=libbz2 приставка за Kerfuffle +Comment[bs]=Priključak libbz2 za Kerfafl +Comment[ca]=Connector del libbz2 pel Kerfuffle +Comment[ca@valencia]=Connector del libbz2 pel Kerfuffle +Comment[cs]=libbz2 modul pro Kerfuffle +Comment[da]=libbz2-plugin til Kerfuffle +Comment[de]=libgz2-Modul für Kerfuffle +Comment[el]=πρόσθετο libz2 για τo Kerfuffle +Comment[en_GB]=libbz2 plugin for Kerfuffle +Comment[es]=Complemento libbz2 para Kerfuffle +Comment[et]=Kerfuffle'i libbz2 plugin +Comment[eu]=Kerfuffle-rentzako libbz2 plugina +Comment[fi]=libbz2-pakkaustuki +Comment[fr]=Module externe « libbz2 » pour Kerfuffle +Comment[ga]=Breiseán libgz le haghaidh Kerfuffle +Comment[gl]=Extensión de libbz2 para Kerfuffle +Comment[hr]=Priključak libbz2 za Kerfuffle +Comment[hu]=Libbz2 Kerfuffle-modul +Comment[ia]=plugin de libbz2 per Kerfuffle +Comment[id]=Pengaya libbz2 untuk Kerfuffle +Comment[it]=Estensione libbz2 per Kerfuffle +Comment[ja]=Kerfuffle のための libgz2 プラグイン +Comment[kk]=Kerfuffle-ге арналған libbz2 плагині +Comment[km]=កម្មវិធី​ជំនួយ libbz2 សម្រាប់ Kerfuffle +Comment[ko]=Kerfuffle을 위한 libbz2 플러그인 +Comment[lt]=libbz2 Kerfuffle priedas +Comment[lv]=Kerfuffle libz2 spraudnis +Comment[mr]=कर्फफल करिता लिब-बिझेड२ प्लगइन +Comment[nb]=libbz2 pprogramtillegg for Kerfuffle +Comment[nds]=Libbz2-Moduul för Kerfuffle +Comment[nl]=libbz2 plug-in voor Kerfuffle +Comment[nn]=libzip2-programtillegg til Kerfuffle +Comment[pl]=wtyczka libbz2 dla Kerfuffle +Comment[pt]='Plugin' da 'libbz2' para o Kerfuffle +Comment[pt_BR]=Plugin libbz2 para o Kerfuffle +Comment[ro]=Modul libbz2 pentru Kerfuffle +Comment[ru]=Модуль libbz2 для Kerfuffle +Comment[sk]=libbz2 modul pre Kerfuffle +Comment[sl]=Vstavek libbz2 za Kerfuffle +Comment[sq]=libbz2 plugin për Kerfuffle +Comment[sr]=Прикључак libbz2 за Керфафл +Comment[sr@ijekavian]=Прикључак libbz2 за Керфафл +Comment[sr@ijekavianlatin]=Priključak libbz2 za Kerfuffle +Comment[sr@latin]=Priključak libbz2 za Kerfuffle +Comment[sv]=Libbz2-insticksprogram för Kerfuffle +Comment[th]=ส่วนเสริมของ Kerfuffle สำหรับใช้งานไลบรารี libbz2 +Comment[tr]=Kerfuffle için libbz2 eklentisi +Comment[uk]=Додаток libbz2 для Kerfuffle +Comment[x-test]=xxlibbz2 plugin for Kerfufflexx +Comment[zh_CN]=Kerfuffle 的 libbz2 插件 +Comment[zh_TW]=Kerfuffle 的 libbz2 外掛程式 +MimeType=application/x-bzip; diff --git a/ark/plugins/libsinglefileplugin/kerfuffle_libgz.desktop b/ark/plugins/libsinglefileplugin/kerfuffle_libgz.desktop new file mode 100644 index 00000000..ae60b508 --- /dev/null +++ b/ark/plugins/libsinglefileplugin/kerfuffle_libgz.desktop @@ -0,0 +1,126 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=Kerfuffle/Plugin +X-KDE-Library=kerfuffle_libgz +X-KDE-PluginInfo-Author=Harald Hvaal +X-KDE-PluginInfo-Email=haraldhv@stud.ntnu.no +X-KDE-PluginInfo-Name=kerfuffle_libgz +X-KDE-PluginInfo-Version=0.0.1 +X-KDE-PluginInfo-Website=http://www.kde.org +X-KDE-PluginInfo-License=BSD +X-KDE-Priority=100 +X-KDE-Kerfuffle-APIRevision=1 +#X-KDE-Kerfuffle-ReadWrite=true +Name=kerfuffle_libgz +Name[ar]=kerfuffle_libgz +Name[ast]=krfuffle_libgz +Name[bg]=kerfuffle_libgz +Name[bs]=kerfuffle_libgz +Name[ca]=kerfuffle_libgz +Name[ca@valencia]=kerfuffle_libgz +Name[cs]=kerfuffle_libgz +Name[da]=kerfuffle_libgz +Name[de]=kerfuffle_libgz +Name[el]=kerfuffle_libgz +Name[en_GB]=kerfuffle_libgz +Name[es]=kerfuffle_libgz +Name[et]=kerfuffle_libgz +Name[eu]=kerfuffle_libgz +Name[fi]=kerfuffle_libgz +Name[fr]=kerfuffle_libgz +Name[ga]=kerfuffle_libgz +Name[gl]=kerfuffle_libgz +Name[hne]=करफुफल_लिबजीजेड +Name[hr]=kerfuffle_libgz +Name[hu]=kerfuffle_libgz +Name[ia]=kerfuffle_libgz +Name[id]=kerfuffle_libgz +Name[it]=kerfuffle_libgz +Name[ja]=kerfuffle_libgz +Name[kk]=kerfuffle_libgz +Name[km]=kerfuffle_libgz +Name[ko]=kerfuffle_libgz +Name[lt]=kerfuffle_libgz +Name[lv]=kerfuffle_libgz +Name[mr]=कर्फफल_लिब-जिझेड +Name[nb]=kerfuffle_libgz +Name[nds]=kerfuffle_libgz +Name[nl]=kerfuffle_libgz +Name[nn]=kerfuffle_libgz +Name[pa]=kerfuffle_libgz +Name[pl]=kerfuffle_libgz +Name[pt]=kerfuffle_libgz +Name[pt_BR]=kerfuffle_libgz +Name[ro]=kerfuffle_libgz +Name[ru]=kerfuffle_libgz +Name[sk]=kerfuffle_libgz +Name[sl]=kerfuffle_libgz +Name[sq]=kerfuffle_libgz +Name[sr]=kerfuffle_libgz +Name[sr@ijekavian]=kerfuffle_libgz +Name[sr@ijekavianlatin]=kerfuffle_libgz +Name[sr@latin]=kerfuffle_libgz +Name[sv]=Kerfuffle Libgz +Name[th]=kerfuffle_libgz +Name[tr]=kerfuffle_libgz +Name[uk]=kerfuffle_libgz +Name[wa]=kerfuffle_libgz +Name[x-test]=xxkerfuffle_libgzxx +Name[zh_CN]=kerfuffle_libgz +Name[zh_TW]=kerfuffle_libgz +Comment=libgz plugin for Kerfuffle +Comment[ar]=ملحقlibgz لـ Kerfuffle +Comment[ast]=Aplicación libgz pa Kerfuffle +Comment[bg]=Приствка libgz за Kerfuffle +Comment[bs]=Priključak libgz za Kerfafl +Comment[ca]=Connector del libgz pel Kerfuffle +Comment[ca@valencia]=Connector del libgz pel Kerfuffle +Comment[cs]=libgz modul pro Kerfuffle +Comment[da]=libgz-plugin til Kerfuffle +Comment[de]=libgz-Modul für Kerfuffle +Comment[el]=πρόσθετο libgz για τη Kerfuffle +Comment[en_GB]=libgz plugin for Kerfuffle +Comment[es]=Complemento libgz para Kerfuffle +Comment[et]=Kerfuffle'i libgz plugin +Comment[eu]=Kerfuffle-rentzako libgz plugina +Comment[fi]=libgz-pakkaustuki +Comment[fr]=Module externe « libgz » pour Kerfuffle +Comment[ga]=Breiseán libgz le haghaidh Kerfuffle +Comment[gl]=Extensión de libgz para Kerfuffle +Comment[hne]=करफुफल बर लिबजीजेड प्लगइन +Comment[hr]=Priključak libgz za Kerfuffle +Comment[hu]=Libgz Kerfuffle-modul +Comment[ia]=plugin de libgz per Kerfuffle +Comment[id]=Pengaya libgz untuk Kerfuffle +Comment[it]=Estensione libgz per Kerfuffle +Comment[ja]=Kerfuffle のための libgz プラグイン +Comment[kk]=Kerfuffle-ге арналған libgz плагині +Comment[km]=កម្មវិធី​ជំនួយ libgz សម្រាប់ Kerfuffle +Comment[ko]=Kerfuffle을 위한 libgz 플러그인 +Comment[lt]=libgz Kerfuffle priedas +Comment[lv]=Kerfuffle libgz spraudnis +Comment[mr]=कर्फफल करिता लिब-जिझेड प्लगइन +Comment[nb]=libgz pprogramtillegg for Kerfuffle +Comment[nds]=Libgz-Moduul för Kerfuffle +Comment[nl]=libgz-plug-in voor Kerfuffle +Comment[nn]=libgz-programtillegg til Kerfuffle +Comment[pl]=wtyczka libgz dla Kerfuffle +Comment[pt]='Plugin' da 'libgz' para o Kerfuffle +Comment[pt_BR]=Plugin libgz para a Kerfuffle +Comment[ro]=Modul libgz pentru Kerfuffle +Comment[ru]=Модуль libgz для Kerfuffle +Comment[sk]=libgz modul pre Kerfuffle +Comment[sl]=Vstavek libgz za Kerfuffle +Comment[sq]=libgz plugin për Kerfuffle +Comment[sr]=Прикључак libgz за Керфафл +Comment[sr@ijekavian]=Прикључак libgz за Керфафл +Comment[sr@ijekavianlatin]=Priključak libgz za Kerfuffle +Comment[sr@latin]=Priključak libgz za Kerfuffle +Comment[sv]=Libgz-insticksprogram för Kerfuffle +Comment[th]=ส่วนเสริมของ Kerfuffle สำหรับใช้งานไลบรารี libgz +Comment[tr]=Kerfuffle için libgz eklentisi +Comment[uk]=Додаток libgz для Kerfuffle +Comment[x-test]=xxlibgz plugin for Kerfufflexx +Comment[zh_CN]=Kerfuffle 的 libgz 插件 +Comment[zh_TW]=Kerfuffle 的 libgz 外掛程式 +MimeType=application/x-gzip; diff --git a/ark/plugins/libsinglefileplugin/kerfuffle_libxz.desktop b/ark/plugins/libsinglefileplugin/kerfuffle_libxz.desktop new file mode 100644 index 00000000..6cc2f82b --- /dev/null +++ b/ark/plugins/libsinglefileplugin/kerfuffle_libxz.desktop @@ -0,0 +1,124 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=Kerfuffle/Plugin +X-KDE-Library=kerfuffle_libxz +X-KDE-PluginInfo-Author=Raphael Kubo da Costa +X-KDE-PluginInfo-Email=rakuco@FreeBSD.org +X-KDE-PluginInfo-Name=kerfuffle_libxz +X-KDE-PluginInfo-Version=0.0.1 +X-KDE-PluginInfo-Website=http://www.kde.org +X-KDE-PluginInfo-License=BSD +X-KDE-Priority=100 +X-KDE-Kerfuffle-APIRevision=1 +#X-KDE-Kerfuffle-ReadWrite=true +Name=kerfuffle_libxz +Name[ar]=kerfuffle_libxz +Name[ast]=kerfuffle_libxz +Name[bg]=kerfuffle_libxz +Name[bs]=kerfuffle_libxz +Name[ca]=kerfuffle_libxz +Name[ca@valencia]=kerfuffle_libxz +Name[cs]=kerfuffle_libxz +Name[da]=kerfuffle_libxz +Name[de]=kerfuffle_libxz +Name[el]=kerfuffle_libxz +Name[en_GB]=kerfuffle_libxz +Name[es]=kerfuffle_libxz +Name[et]=kerfuffle_libxz +Name[eu]=kerfuffle_libxz +Name[fi]=kerfuffle_libxz +Name[fr]=kerfuffle_libxz +Name[ga]=kerfuffle_libxz +Name[gl]=kerfuffle_libxz +Name[hr]=kerfuffle_libxz +Name[hu]=kerfuffle_libxz +Name[ia]=kerfuffle_libxz +Name[id]=kerfuffle_libxz +Name[it]=kerfuffle_libxz +Name[ja]=kerfuffle_libxz +Name[kk]=kerfuffle_libxz +Name[km]=kerfuffle_libxz +Name[ko]=kerfuffle_libxz +Name[lt]=kerfuffle_libxz +Name[lv]=kerfuffle_libxz +Name[mr]=कर्फफल_लिब-एक्सझेड +Name[nb]=kerfuffle_libxz +Name[nds]=kerfuffle_libxz +Name[nl]=kerfuffle_libxz +Name[nn]=kerfuffle_libxz +Name[pa]=kerfuffle_libxz +Name[pl]=kerfuffle_libxz +Name[pt]=kerfuffle_libxz +Name[pt_BR]=kerfuffle_libxz +Name[ro]=kerfuffle_libxz +Name[ru]=kerfuffle_libxz +Name[sk]=kerfuffle_libxz +Name[sl]=kerfuffle_libxz +Name[sq]=kerfuffle_libxz +Name[sr]=kerfuffle_libxz +Name[sr@ijekavian]=kerfuffle_libxz +Name[sr@ijekavianlatin]=kerfuffle_libxz +Name[sr@latin]=kerfuffle_libxz +Name[sv]=Kerfuffle Libxz +Name[th]=kerfuffle_libxz +Name[tr]=kerfuffle_libxz +Name[uk]=kerfuffle_libxz +Name[wa]=kerfuffle_libxz +Name[x-test]=xxkerfuffle_libxzxx +Name[zh_CN]=kerfuffle_libxz +Name[zh_TW]=kerfuffle_libxz +Comment=libxz plugin for Kerfuffle +Comment[ar]=ملحقlibxz لـ Kerfuffle +Comment[ast]=Complementu libxz pa Kerfuffle +Comment[bg]=libxz приставка за Kerfuffle +Comment[bs]=Priključak libxz za Kerfafl +Comment[ca]=Connector del libxz pel Kerfuffle +Comment[ca@valencia]=Connector del libxz pel Kerfuffle +Comment[cs]=libxz modul pro Kerfuffle +Comment[da]=libxz-plugin til Kerfuffle +Comment[de]=libxz-Modul für Kerfuffle +Comment[el]=πρόσθετο libxz για τo Kerfuffle +Comment[en_GB]=libxz plugin for Kerfuffle +Comment[es]=Complemento libxz para Kerfuffle +Comment[et]=Kerfuffle'i libxz plugin +Comment[eu]=Kerfuffle-rentzako libxz plugina +Comment[fi]=libxz-pakkaustuki +Comment[fr]=Module externe « libxz » pour Kerfuffle +Comment[ga]=Breiseán libxz le haghaidh Kerfuffle +Comment[gl]=Extensión de libxz para Kerfuffle +Comment[hr]=Priključak libxz za Kerfuffle +Comment[hu]=Libxz Kerfuffle-modul +Comment[ia]=plugin de libxz per Kerfuffle +Comment[id]=Pengaya libxz untuk Kerfuffle +Comment[it]=Estensione libxz per Kerfuffle +Comment[ja]=Kerfuffle のための libxz プラグイン +Comment[kk]=Kerfuffle-ге арналған libxz плагині +Comment[km]=កម្មវិធី​ជំនួយ​ libxz សម្រាប់ Kerfuffle +Comment[ko]=Kerfuffle을 위한 libxz 플러그인 +Comment[lt]=libxz Kerfuffle priedas +Comment[lv]=Kerfuffle libxz spraudnis +Comment[mr]=कर्फफल करिता लिब-एक्सझेड प्लगइन +Comment[nb]=libxz pprogramtillegg for Kerfuffle +Comment[nds]=Libxz-Moduul för Kerfuffle +Comment[nl]=libxz plug-in voor Kerfuffle +Comment[nn]=libxz-programtillegg til Kerfuffle +Comment[pl]=wtyczka libxz dla Kerfuffle +Comment[pt]='Plugin' da 'libxz' para o Kerfuffle +Comment[pt_BR]=Plugin libxz para o Kerfuffle +Comment[ro]=Modul libxz pentru Kerfuffle +Comment[ru]=Модуль libxz для Kerfuffle +Comment[sk]=libxz modul pre Kerfuffle +Comment[sl]=Vstavek libxz za Kerfuffle +Comment[sq]=libxz plugin për Kerfuffle +Comment[sr]=Прикључак libxz за Керфафл +Comment[sr@ijekavian]=Прикључак libxz за Керфафл +Comment[sr@ijekavianlatin]=Priključak libxz za Kerfuffle +Comment[sr@latin]=Priključak libxz za Kerfuffle +Comment[sv]=Libxz-insticksprogram för Kerfuffle +Comment[th]=ส่วนเสริมของ Kerfuffle สำหรับใช้งานไลบรารี libxz +Comment[tr]=Kerfuffle için libxz eklentisi +Comment[uk]=Додаток libxz для Kerfuffle +Comment[x-test]=xxlibxz plugin for Kerfufflexx +Comment[zh_CN]=Kerfuffle 的 libxz 插件 +Comment[zh_TW]=Kerfuffle 的 libxz 外掛程式 +MimeType=application/x-lzma;application/x-xz; diff --git a/ark/plugins/libsinglefileplugin/singlefileplugin.cpp b/ark/plugins/libsinglefileplugin/singlefileplugin.cpp new file mode 100644 index 00000000..ac5a2f5e --- /dev/null +++ b/ark/plugins/libsinglefileplugin/singlefileplugin.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2009 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "singlefileplugin.h" +#include "kerfuffle/kerfuffle_export.h" +#include "kerfuffle/queries.h" + +#include +#include +#include +#include + +#include +#include +#include + +LibSingleFileInterface::LibSingleFileInterface(QObject *parent, const QVariantList & args) + : Kerfuffle::ReadOnlyArchiveInterface(parent, args) +{ +} + +LibSingleFileInterface::~LibSingleFileInterface() +{ +} + +bool LibSingleFileInterface::copyFiles(const QList & files, const QString & destinationDirectory, Kerfuffle::ExtractionOptions options) +{ + Q_UNUSED(files) + Q_UNUSED(options) + + QString outputFileName = destinationDirectory; + if (!destinationDirectory.endsWith(QLatin1Char('/'))) { + outputFileName += QLatin1Char('/'); + } + outputFileName += uncompressedFileName(); + + outputFileName = overwriteFileName(outputFileName); + if (outputFileName.isEmpty()) { + return true; + } + + kDebug() << "Extracting to" << outputFileName; + + QFile outputFile(outputFileName); + if (!outputFile.open(QIODevice::WriteOnly)) { + kDebug() << "Failed to open output file" << outputFile.errorString(); + emit error(i18nc("@info", "Ark could not extract %1.", outputFile.fileName())); + + return false; + } + + QIODevice *device = KFilterDev::deviceForFile(filename(), m_mimeType, false); + if (!device) { + kDebug() << "Could not create KFilterDev"; + emit error(i18nc("@info", "Ark could not open %1 for extraction.", filename())); + + return false; + } + + device->open(QIODevice::ReadOnly); + + qint64 bytesRead; + QByteArray dataChunk(1024*16, '\0'); // 16Kb + + while (true) { + bytesRead = device->read(dataChunk.data(), dataChunk.size()); + + if (bytesRead == -1) { + emit error(i18nc("@info", "There was an error while reading %1 during extraction.", filename())); + break; + } else if (bytesRead == 0) { + break; + } + + outputFile.write(dataChunk.data(), bytesRead); + } + + delete device; + + return true; +} + +bool LibSingleFileInterface::list() +{ + kDebug(); + + const QString filename = uncompressedFileName(); + + Kerfuffle::ArchiveEntry e; + + e[Kerfuffle::FileName] = filename; + e[Kerfuffle::InternalID] = filename; + + emit entry(e); + + return true; +} + +QString LibSingleFileInterface::overwriteFileName(QString& filename) +{ + QString newFileName(filename); + + while (QFile::exists(newFileName)) { + Kerfuffle::OverwriteQuery query(newFileName); + + query.setMultiMode(false); + emit userQuery(&query); + query.waitForResponse(); + + if ((query.responseCancelled()) || (query.responseSkip())) { + return QString(); + } else if (query.responseOverwrite()) { + break; + } else if (query.responseRename()) { + newFileName = query.newFilename(); + } + } + + return newFileName; +} + +const QString LibSingleFileInterface::uncompressedFileName() const +{ + QString uncompressedName(QFileInfo(filename()).fileName()); + + foreach(const QString & extension, m_possibleExtensions) { + kDebug() << extension; + + if (uncompressedName.endsWith(extension, Qt::CaseInsensitive)) { + uncompressedName.chop(extension.size()); + return uncompressedName; + } + } + + return uncompressedName + QLatin1String( ".uncompressed" ); +} + +#include "singlefileplugin.moc" diff --git a/ark/plugins/libsinglefileplugin/singlefileplugin.h b/ark/plugins/libsinglefileplugin/singlefileplugin.h new file mode 100644 index 00000000..1cbce40f --- /dev/null +++ b/ark/plugins/libsinglefileplugin/singlefileplugin.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2009 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SINGLEFILEPLUGIN_H +#define SINGLEFILEPLUGIN_H + +#include "kerfuffle/archiveinterface.h" + +class LibSingleFileInterface : public Kerfuffle::ReadOnlyArchiveInterface +{ + Q_OBJECT + +public: + LibSingleFileInterface(QObject *parent, const QVariantList & args); + virtual ~LibSingleFileInterface(); + + virtual bool list(); + virtual bool copyFiles(const QList & files, const QString & destinationDirectory, Kerfuffle::ExtractionOptions options); + +protected: + const QString uncompressedFileName() const; + QString overwriteFileName(QString& filename); + + QString m_mimeType; + QStringList m_possibleExtensions; +}; + +#endif // SINGLEFILEPLUGIN_H diff --git a/ark/plugins/libsinglefileplugin/xzplugin.cpp b/ark/plugins/libsinglefileplugin/xzplugin.cpp new file mode 100644 index 00000000..5805dbbd --- /dev/null +++ b/ark/plugins/libsinglefileplugin/xzplugin.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2009 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "xzplugin.h" +#include "kerfuffle/kerfuffle_export.h" + +#include + +LibXzInterface::LibXzInterface(QObject *parent, const QVariantList & args) + : LibSingleFileInterface(parent, args) +{ + m_mimeType = QLatin1String( "application/x-lzma" ); + m_possibleExtensions.append(QLatin1String( ".lzma" )); + m_possibleExtensions.append(QLatin1String( ".xz" )); +} + +LibXzInterface::~LibXzInterface() +{ +} + +KERFUFFLE_EXPORT_PLUGIN(LibXzInterface) + +#include "xzplugin.moc" diff --git a/ark/plugins/libsinglefileplugin/xzplugin.h b/ark/plugins/libsinglefileplugin/xzplugin.h new file mode 100644 index 00000000..bf5c0e52 --- /dev/null +++ b/ark/plugins/libsinglefileplugin/xzplugin.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2009 Raphael Kubo da Costa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XZPLUGIN_H +#define XZPLUGIN_H + +#include "singlefileplugin.h" + +class LibXzInterface : public LibSingleFileInterface +{ + Q_OBJECT + +public: + LibXzInterface(QObject *parent, const QVariantList & args); + virtual ~LibXzInterface(); +}; + +#endif // XZPLUGIN_H