From: Sandro Knauß Date: Mon, 4 Feb 2019 14:58:32 +0000 (+0000) Subject: Import kdav_18.08.3.orig.tar.xz X-Git-Tag: archive/raspbian/1%5.102.0-1+rpi1~1^2^2^2~2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=820e519d63b5bbfe9a90594acfb930a59d2c8540;p=kdav.git Import kdav_18.08.3.orig.tar.xz [dgit import orig kdav_18.08.3.orig.tar.xz] --- 820e519d63b5bbfe9a90594acfb930a59d2c8540 diff --git a/.arcconfig b/.arcconfig new file mode 100644 index 0000000..3ff2b47 --- /dev/null +++ b/.arcconfig @@ -0,0 +1,3 @@ +{ + "phabricator.uri": "https://phabricator.kde.org/project/profile/34/" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d1af1f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# Ignore the following files +/build/ +CMakeLists.txt.user* +*.unc-backup* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2380cb8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,82 @@ +cmake_minimum_required(VERSION 3.0) +set(PIM_VERSION "5.9.3") + +project(libkdav VERSION ${PIM_VERSION}) + +set(LIBKDAV_VERSION ${PIM_VERSION}) + +set(KF5_VERSION "5.47.0") + +find_package(ECM ${KF5_VERSION} CONFIG REQUIRED) +set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) + +include(CMakeFindDependencyMacro) +include(ECMSetupVersion) +include(GenerateExportHeader) +include(ECMGenerateHeaders) +include(ECMGeneratePriFile) +include(FeatureSummary) +include(KDEInstallDirs) +include(KDECMakeSettings) +include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) +include(ECMAddTests) +include(ECMInstallIcons) +include(ECMQtDeclareLoggingCategory) +include(ECMCoverageOption) + +set(QT_REQUIRED_VERSION "5.9.0") + +find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Gui XmlPatterns Test) + +find_package(KF5 ${KF5_VERSION} REQUIRED CoreAddons KIO I18n) + +# setup lib + +add_definitions(-DQT_NO_CAST_FROM_ASCII) +add_definitions(-DQT_NO_CAST_TO_ASCII) +add_definitions(-DQT_NO_URL_CAST_FROM_STRING) +add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) +add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT) + + +ecm_setup_version(${LIBKDAV_VERSION} VARIABLE_PREFIX KDAV + VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kpimkdav_version.h" + PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KPimKDAVConfigVersion.cmake" + SOVERSION 5 + ) + +set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KPimKDAV") + +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/KPimKDAVConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/KPimKDAVConfig.cmake" + INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} + ) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/KPimKDAVConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/KPimKDAVConfigVersion.cmake" + DESTINATION "${CMAKECONFIG_INSTALL_DIR}" + COMPONENT Devel + ) + +install(EXPORT KPimKDAVTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KPimKDAVTargets.cmake NAMESPACE KPim::) + +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/kpimkdav_version.h + DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim COMPONENT Devel + ) + +install(FILES kdav.categories DESTINATION ${KDE_INSTALL_CONFDIR}) + +add_subdirectory(src) +if(BUILD_TESTING) + add_subdirectory(autotests) + add_subdirectory(test) +endif() + +feature_summary(WHAT ALL + INCLUDE_QUIET_PACKAGES + FATAL_ON_MISSING_REQUIRED_PACKAGES +) +ki18n_install(po) diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..dcfa4c2 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) year 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/KPimKDAVConfig.cmake.in b/KPimKDAVConfig.cmake.in new file mode 100644 index 0000000..465c247 --- /dev/null +++ b/KPimKDAVConfig.cmake.in @@ -0,0 +1,6 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(KF5CoreAddons "@KF5_VERSION@") + +include("${CMAKE_CURRENT_LIST_DIR}/KPimKDAVTargets.cmake") diff --git a/README b/README new file mode 100644 index 0000000..6d224ca --- /dev/null +++ b/README @@ -0,0 +1,50 @@ +==== What's this ? ==== + +This is an DAV protocol implemention with KJobs. + +Calendars and todos are supported, using either GroupDAV +or CalDAV, and contacts are supported using GroupDAV or +CardDAV. + + +==== Usage ==== + +It should be pretty straightforward. The URL to use should be, if possible, +the principals URL for your server (CalDAV / CardDAV) or the parent +collection of your calendars to allow discovery. + + +==== Tested with / known bugs ==== + +Here is a list of servers tested with this resource with the URLs used. +Feel free to contact the author(s) if you successfully tested a configuration +not listed here. + +* Egroupware (1.6.002) + https://host/groupdav.php + - GroupDAV working. + - CalDAV working. + - CardDAV working. + +* SOGo (version 1.0.4, 1.1.0 and version at http://sogo-demo.inverse.ca/) + https://host/SOGo/dav//Calendar and https://host/SOGo/dav//Contacts + - GroupDAV calendar working, but the timezone data in the ICalendar + generated by KCal seems misinterpreted by SOGo : every event is + shifted by the timezone offset (at least test with TZ Europe/Paris, + feel free to send your results to the author(s). This seems resolved + with the demo version made available by Inverse.ca. + - CalDAV working, with the same bug. + - CardDAV working. + +* Davical (version 0.9.7.6) + https://host/caldav.php/principals/users/ + - CalDAV working. + +* Zimbra Open-Source edition (version 5.0.18), + https://host/principals/users/ + - Caldav mostly working : retrieval of shared calendars that contain a lot + of events fails with a 500 server error. + +* Google calendar + https://www.google.com/calendar/dav//events + - CalDAV working. diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt new file mode 100644 index 0000000..20106c1 --- /dev/null +++ b/autotests/CMakeLists.txt @@ -0,0 +1,32 @@ +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) +add_definitions(-DAUTOTEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data") + +ecm_add_test(davcollectiontest.cpp + TEST_NAME davcollection + NAME_PREFIX "kdav-" + LINK_LIBRARIES KPim::KDAV Qt5::Test Qt5::Core Qt5::Gui +) + +ecm_add_test(davitemtest.cpp + TEST_NAME davitem + NAME_PREFIX "kdav-" + LINK_LIBRARIES KPim::KDAV Qt5::Test Qt5::Core +) + +ecm_add_test(davurltest.cpp + TEST_NAME davurl + NAME_PREFIX "kdav-" + LINK_LIBRARIES KPim::KDAV Qt5::Test Qt5::Core +) + +ecm_add_test(davitemfetchjobtest.cpp fakeserver.cpp + TEST_NAME davitemfetchjob + NAME_PREFIX "kdav-" + LINK_LIBRARIES KPim::KDAV Qt5::Test Qt5::Core Qt5::Network +) + +ecm_add_test(davitemslistjobtest.cpp fakeserver.cpp + TEST_NAME davitemslistjob + NAME_PREFIX "kdav-" + LINK_LIBRARIES KPim::KDAV Qt5::Test Qt5::Core Qt5::Network +) diff --git a/autotests/data/dataitemfetchjob.txt b/autotests/data/dataitemfetchjob.txt new file mode 100644 index 0000000..26588f9 --- /dev/null +++ b/autotests/data/dataitemfetchjob.txt @@ -0,0 +1,17 @@ +C: GET /item HTTP/1.1 +C: User-Agent: KDE DAV groupware client +S: HTTP/1.0 200 OK +S: Date: Wed, 04 Jan 2017 18:26:48 GMT +S: Last-Modified: Wed, 04 Jan 2017 18:26:47 GMT +S: ETag: 7a33141f192d904d-47 +S: Content-Type: text/x-vcard; charset=utf-8 +D: BEGIN:VCARD +D: VERSION:3.0 +D: PRODID:-//Kolab//iRony DAV Server 0.3.1//Sabre//Sabre VObject 2.1.7//EN +D: UID:12345678-1234-1234-1234-123456789abc +D: FN:John2 Doe +D: N:Doe;John2;;; +D: EMAIL;TYPE=INTERNET;TYPE=HOME:john2.doe@example.com +D: REV;VALUE=DATE-TIME:20170104T182647Z +D: END:VCARD +X diff --git a/autotests/davcollectiontest.cpp b/autotests/davcollectiontest.cpp new file mode 100644 index 0000000..660f292 --- /dev/null +++ b/autotests/davcollectiontest.cpp @@ -0,0 +1,111 @@ +/* + Copyright (c) 2017 Sandro Knauß + + 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 "davcollectiontest.h" + +#include +#include + +#include +#include + +void DavCollectionTest::createEmpty() +{ + KDAV::DavCollection davCollection; + + QCOMPARE(davCollection.url().protocol(), KDAV::CalDav); + QCOMPARE(davCollection.CTag(), QString()); + QCOMPARE(davCollection.displayName(), QString()); + QCOMPARE(davCollection.color(), QColor()); + QCOMPARE(davCollection.contentTypes(), KDAV::DavCollection::ContentTypes()); + QCOMPARE(davCollection.privileges(), KDAV::Privileges()); +} + +void DavCollectionTest::storeTest() +{ + QUrl url(QStringLiteral("test://me:pw@test")); + KDAV::DavUrl davUrl(url, KDAV::CardDav); + KDAV::DavCollection davCollection(davUrl, QStringLiteral("myname"), KDAV::DavCollection::Events | KDAV::DavCollection::Todos); + + QCOMPARE(davCollection.url().protocol(), KDAV::CardDav); + QCOMPARE(davCollection.url().url(), url); + QCOMPARE(davCollection.CTag(), QString()); + QCOMPARE(davCollection.displayName(), QStringLiteral("myname")); + QCOMPARE(davCollection.color(), QColor()); + QCOMPARE(davCollection.contentTypes(), KDAV::DavCollection::Events | KDAV::DavCollection::Todos); + QCOMPARE(davCollection.privileges(), KDAV::All); +} + +void DavCollectionTest::setTest() +{ + QUrl url(QStringLiteral("test://me:pw@test")); + KDAV::DavUrl davUrl(url, KDAV::CardDav); + KDAV::DavCollection davCollection; + + davCollection.setUrl(davUrl); + davCollection.setCTag(QStringLiteral("ctag")); + davCollection.setDisplayName(QStringLiteral("myname")); + davCollection.setColor(QColor(1,2,3)); + davCollection.setContentTypes(KDAV::DavCollection::Events | KDAV::DavCollection::Todos); + davCollection.setPrivileges(KDAV::Read | KDAV::Write); + + QCOMPARE(davCollection.url().protocol(), KDAV::CardDav); + QCOMPARE(davCollection.url().url(), url); + QCOMPARE(davCollection.CTag(), QStringLiteral("ctag")); + QCOMPARE(davCollection.displayName(), QStringLiteral("myname")); + QCOMPARE(davCollection.color(), QColor(1,2,3)); + QCOMPARE(davCollection.contentTypes(), KDAV::DavCollection::Events | KDAV::DavCollection::Todos); + QCOMPARE(davCollection.privileges(), KDAV::Read | KDAV::Write); +} + +void DavCollectionTest::copyTest() +{ + KDAV::DavCollection davCollection; + + QUrl url(QStringLiteral("test://me:pw@test")); + KDAV::DavUrl davUrl(url, KDAV::CardDav); + + davCollection.setUrl(davUrl); + davCollection.setCTag(QStringLiteral("ctag")); + davCollection.setDisplayName(QStringLiteral("myname")); + davCollection.setColor(QColor(1,2,3)); + davCollection.setContentTypes(KDAV::DavCollection::Events | KDAV::DavCollection::Todos); + davCollection.setPrivileges(KDAV::Read | KDAV::Write); + + KDAV::DavCollection copy1(davCollection); + QCOMPARE(copy1.url().protocol(), davCollection.url().protocol()); + QCOMPARE(copy1.url().url(), davCollection.url().url()); + QCOMPARE(copy1.CTag(), davCollection.CTag()); + QCOMPARE(copy1.displayName(), davCollection.displayName()); + QCOMPARE(copy1.color(), davCollection.color()); + QCOMPARE(copy1.contentTypes(), davCollection.contentTypes()); + QCOMPARE(copy1.privileges(), davCollection.privileges()); + + KDAV::DavCollection copy2; + copy2 = davCollection; + + QCOMPARE(copy2.url().protocol(), davCollection.url().protocol()); + QCOMPARE(copy2.url().url(), davCollection.url().url()); + QCOMPARE(copy2.CTag(), davCollection.CTag()); + QCOMPARE(copy2.displayName(), davCollection.displayName()); + QCOMPARE(copy2.color(), davCollection.color()); + QCOMPARE(copy2.contentTypes(), davCollection.contentTypes()); + QCOMPARE(copy2.privileges(), davCollection.privileges()); +} + +QTEST_MAIN(DavCollectionTest) diff --git a/autotests/davcollectiontest.h b/autotests/davcollectiontest.h new file mode 100644 index 0000000..caf98fb --- /dev/null +++ b/autotests/davcollectiontest.h @@ -0,0 +1,35 @@ +/* + Copyright (c) 2017 Sandro Knauß + + 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 DAVCOLLECTION_TEST_H +#define DAVCOLLECTION_TEST_H + +#include + +class DavCollectionTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void createEmpty(); + void storeTest(); + void setTest(); + void copyTest(); +}; + +#endif diff --git a/autotests/davitemfetchjobtest.cpp b/autotests/davitemfetchjobtest.cpp new file mode 100644 index 0000000..9e1c6fc --- /dev/null +++ b/autotests/davitemfetchjobtest.cpp @@ -0,0 +1,57 @@ +/* + Copyright (c) 2017 Sandro Knauß + + 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 "davitemfetchjobtest.h" +#include "fakeserver.h" + +#include + +#include + +void DavItemFetchJobTest::runSuccessfullTest() +{ + FakeServer fakeServer; + QUrl url(QStringLiteral("http://localhost/item")); + url.setPort(fakeServer.port()); + KDAV::DavUrl davUrl(url, KDAV::CardDav); + + KDAV::DavItem item(davUrl, QString(), QByteArray(), QString()); + + auto job = new KDAV::DavItemFetchJob(item); + + fakeServer.addScenarioFromFile(QLatin1String(AUTOTEST_DATA_DIR)+QStringLiteral("/dataitemfetchjob.txt")); + fakeServer.startAndWait(); + job->exec(); + fakeServer.quit(); + + QVERIFY(fakeServer.isAllScenarioDone()); + QCOMPARE(job->error(), 0); + + QCOMPARE(item.data(), QByteArray()); + QCOMPARE(item.etag(), QString()); + QCOMPARE(item.contentType(), QString()); + + item = job->item(); + QByteArray data("BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Kolab//iRony DAV Server 0.3.1//Sabre//Sabre VObject 2.1.7//EN\r\nUID:12345678-1234-1234-1234-123456789abc\r\nFN:John2 Doe\r\nN:Doe;John2;;;\r\nEMAIL;TYPE=INTERNET;TYPE=HOME:john2.doe@example.com\r\nREV;VALUE=DATE-TIME:20170104T182647Z\r\nEND:VCARD\r\n"); + QCOMPARE(item.data(), data); + QCOMPARE(item.etag(), QStringLiteral("7a33141f192d904d-47")); + QCOMPARE(item.contentType(), QStringLiteral("text/x-vcard")); + +} + +QTEST_MAIN(DavItemFetchJobTest) diff --git a/autotests/davitemfetchjobtest.h b/autotests/davitemfetchjobtest.h new file mode 100644 index 0000000..3b45902 --- /dev/null +++ b/autotests/davitemfetchjobtest.h @@ -0,0 +1,32 @@ +/* + Copyright (c) 2017 Sandro Knauß + + 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 DAVITEMFETCHJOB_TEST_H +#define DAVITEMFETCHJOB_TEST_H + +#include + +class DavItemFetchJobTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void runSuccessfullTest(); +}; + +#endif diff --git a/autotests/davitemslistjobtest.cpp b/autotests/davitemslistjobtest.cpp new file mode 100644 index 0000000..f7fa281 --- /dev/null +++ b/autotests/davitemslistjobtest.cpp @@ -0,0 +1,46 @@ +/* + Copyright (c) 2017 Sandro Knauß + + 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 "davitemslistjobtest.h" +#include "fakeserver.h" + +#include +#include +#include +#include + +#include + +void DavItemsListJobTest::noMatchingMimetype() +{ + std::shared_ptr cache(new KDAV::EtagCache()); + + QUrl url(QStringLiteral("http://localhost/collection")); + KDAV::DavUrl davUrl(url, KDAV::CardDav); + KDAV::Error error(KDAV::ErrorNumber::ERR_ITEMLIST_NOMIMETYPE, 0, QString(), 0); + + auto job = new KDAV::DavItemsListJob(davUrl, cache); + job->setContentMimeTypes(QStringList() << QStringLiteral("mime/invalid1") << QStringLiteral("mime/invalid2")); + job->exec(); + + QCOMPARE(job->error(), static_cast(KDAV::ErrorNumber::ERR_ITEMLIST_NOMIMETYPE)); + QCOMPARE(job->errorText(), error.errorText()); + +} + +QTEST_MAIN(DavItemsListJobTest) diff --git a/autotests/davitemslistjobtest.h b/autotests/davitemslistjobtest.h new file mode 100644 index 0000000..b4c514f --- /dev/null +++ b/autotests/davitemslistjobtest.h @@ -0,0 +1,32 @@ +/* + Copyright (c) 2017 Sandro Knauß + + 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 DAVITEMSLISTJOB_TEST_H +#define DAVITEMSLISTJOB_TEST_H + +#include + +class DavItemsListJobTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void noMatchingMimetype(); +}; + +#endif diff --git a/autotests/davitemtest.cpp b/autotests/davitemtest.cpp new file mode 100644 index 0000000..7fdb518 --- /dev/null +++ b/autotests/davitemtest.cpp @@ -0,0 +1,115 @@ +/* + Copyright (c) 2017 Sandro Knauß + + 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 "davitemtest.h" + +#include +#include + +#include +#include + +void DavItemTest::createEmpty() +{ + KDAV::DavItem davItem; + + QCOMPARE(davItem.url().toDisplayString(), QString()); + QCOMPARE(davItem.contentType(), QString()); + QCOMPARE(davItem.data(), QByteArray()); + QCOMPARE(davItem.etag(), QString()); +} + + +void DavItemTest::storeTest() +{ + QUrl url(QStringLiteral("test://me:pw@test")); + KDAV::DavUrl davUrl(url, KDAV::CardDav); + KDAV::DavItem davItem(davUrl, QStringLiteral("text/test"), QByteArray("data"), QStringLiteral("991233434-234345")); + + QCOMPARE(davItem.url().protocol(), KDAV::CardDav); + QCOMPARE(davItem.contentType(), QStringLiteral("text/test")); + QCOMPARE(davItem.data(), QByteArray("data")); + QCOMPARE(davItem.etag(), QStringLiteral("991233434-234345")); +} + +void DavItemTest::setTest() +{ + QUrl url(QStringLiteral("test://me:pw@test")); + KDAV::DavUrl davUrl(url, KDAV::CardDav); + KDAV::DavItem davItem; + + davItem.setUrl(davUrl); + davItem.setContentType(QStringLiteral("text/test")); + davItem.setData(QByteArray("data")); + davItem.setEtag(QStringLiteral("991233434-234345")); + + QCOMPARE(davItem.url().protocol(), KDAV::CardDav); + QCOMPARE(davItem.contentType(), QStringLiteral("text/test")); + QCOMPARE(davItem.data(), QByteArray("data")); + QCOMPARE(davItem.etag(), QStringLiteral("991233434-234345")); +} + +void DavItemTest::copyTest() +{ + QUrl url(QStringLiteral("test://me:pw@test")); + KDAV::DavUrl davUrl(url, KDAV::CardDav); + KDAV::DavItem davItem(davUrl, QStringLiteral("text/test"), QByteArray("data"), QStringLiteral("991233434-234345")); + + KDAV::DavItem davItemCopy1(davItem); + QCOMPARE(davItemCopy1.url().protocol(), davItem.url().protocol()); + QCOMPARE(davItemCopy1.url().url(), davItem.url().url()); + QCOMPARE(davItemCopy1.contentType(), davItem.contentType()); + QCOMPARE(davItemCopy1.data(), davItem.data()); + QCOMPARE(davItemCopy1.etag(), davItem.etag()); + + KDAV::DavItem davItemCopy2; + davItemCopy2 = davItem; + QCOMPARE(davItemCopy2.url().protocol(), davItem.url().protocol()); + QCOMPARE(davItemCopy2.url().url(), davItem.url().url()); + QCOMPARE(davItemCopy2.contentType(), davItem.contentType()); + QCOMPARE(davItemCopy2.data(), davItem.data()); + QCOMPARE(davItemCopy2.etag(), davItem.etag()); +} + +void DavItemTest::serializeTest() +{ + KDAV::DavItem davItem1, davItem2; + + QUrl url(QStringLiteral("test://me:pw@test")); + KDAV::DavUrl davUrl(url, KDAV::CardDav); + + davItem1.setUrl(davUrl); + davItem1.setContentType(QStringLiteral("text/test")); + davItem1.setData(QByteArray("data")); + davItem1.setEtag(QStringLiteral("991233434-234345")); + + QByteArray data; + QDataStream s(&data, QIODevice::WriteOnly); + s << davItem1; + + QDataStream t(&data, QIODevice::ReadOnly); + t >> davItem2; + + QCOMPARE(davItem2.url().protocol(), davItem1.url().protocol()); + QCOMPARE(davItem2.url().url(), davItem1.url().url()); + QCOMPARE(davItem2.contentType(), davItem1.contentType()); + QCOMPARE(davItem2.data(), davItem1.data()); + QCOMPARE(davItem2.etag(), davItem1.etag()); +} + +QTEST_MAIN(DavItemTest) diff --git a/autotests/davitemtest.h b/autotests/davitemtest.h new file mode 100644 index 0000000..9c63dcb --- /dev/null +++ b/autotests/davitemtest.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2017 Sandro Knauß + + 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 DAVITEM_TEST_H +#define DAVITEM_TEST_H + +#include + +class DavItemTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void createEmpty(); + void storeTest(); + void setTest(); + void copyTest(); + + void serializeTest(); +}; + +#endif diff --git a/autotests/davurltest.cpp b/autotests/davurltest.cpp new file mode 100644 index 0000000..4c6dffd --- /dev/null +++ b/autotests/davurltest.cpp @@ -0,0 +1,76 @@ +/* + Copyright (c) 2017 Sandro Knauß + + 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 "davurltest.h" + +#include + +#include +#include + +void DavUrlTest::createEmpty() +{ + KDAV::DavUrl davUrl; + + QCOMPARE(davUrl.protocol(), KDAV::CalDav); + QCOMPARE(davUrl.url(), QUrl()); +} + +void DavUrlTest::storeTest() +{ + QUrl url(QStringLiteral("test://me:pw@test")); + KDAV::DavUrl davUrl(url, KDAV::CardDav); + + QCOMPARE(davUrl.protocol(), KDAV::CardDav); + QCOMPARE(davUrl.url(), url); + QCOMPARE(davUrl.toDisplayString(), QStringLiteral("test://test")); +} + +void DavUrlTest::setTest() +{ + QUrl url(QStringLiteral("test://me:pw@test")); + KDAV::DavUrl davUrl; + + davUrl.setProtocol(KDAV::CardDav); + davUrl.setUrl(url); + + QCOMPARE(davUrl.protocol(), KDAV::CardDav); + QCOMPARE(davUrl.url(), url); + QCOMPARE(davUrl.toDisplayString(), QStringLiteral("test://test")); +} + +void DavUrlTest::serializeTest() +{ + KDAV::DavUrl davUrl1, davUrl2; + + QUrl url(QStringLiteral("test://me:pw@test")); + davUrl1.setProtocol(KDAV::CardDav); + davUrl1.setUrl(url); + + QByteArray data; + QDataStream s(&data, QIODevice::WriteOnly); + s << davUrl1; + + QDataStream t(&data, QIODevice::ReadOnly); + t >> davUrl2; + + QCOMPARE(davUrl2.protocol(), davUrl1.protocol()); + QCOMPARE(davUrl2.url(), davUrl1.url()); +} + +QTEST_MAIN(DavUrlTest) diff --git a/autotests/davurltest.h b/autotests/davurltest.h new file mode 100644 index 0000000..163b7b5 --- /dev/null +++ b/autotests/davurltest.h @@ -0,0 +1,36 @@ +/* + Copyright (c) 2017 Sandro Knauß + + 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 DAVURL_TEST_H +#define DAVURL_TEST_H + +#include + +class DavUrlTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void createEmpty(); + void storeTest(); + void setTest(); + + void serializeTest(); +}; + +#endif diff --git a/autotests/fakeserver.cpp b/autotests/fakeserver.cpp new file mode 100644 index 0000000..670c9fd --- /dev/null +++ b/autotests/fakeserver.cpp @@ -0,0 +1,230 @@ +/* + Copyright (C) 2008 Omat Holding B.V. + + Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company + Author: Kevin Ottens + + Copyright (C) 2017 Sandro Kanuß + + 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. +*/ + +// Own +#include "fakeserver.h" + +// Qt +#include +#include + +#include +#include +#include + +FakeServer::FakeServer(QObject *parent) + : QThread(parent) + , m_port(5989) +{ + moveToThread(this); +} + +FakeServer::~FakeServer() +{ + quit(); + wait(); +} + +void FakeServer::startAndWait() +{ + start(); + // this will block until the event queue starts +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + QMetaObject::invokeMethod(this, &FakeServer::started, Qt::BlockingQueuedConnection); +#else + QMetaObject::invokeMethod(this, "started", Qt::BlockingQueuedConnection); +#endif + +} + +void FakeServer::dataAvailable() +{ + QMutexLocker locker(&m_mutex); + + QTcpSocket *socket = qobject_cast(sender()); + Q_ASSERT(socket != nullptr); + + int scenarioNumber = m_clientSockets.indexOf(socket); + + readClientPart(scenarioNumber); + writeServerPart(scenarioNumber); +} + +void FakeServer::newConnection() +{ + QMutexLocker locker(&m_mutex); + + m_clientSockets << m_tcpServer->nextPendingConnection(); + connect(m_clientSockets.last(), &QTcpSocket::readyRead, this, &FakeServer::dataAvailable); +} + +void FakeServer::run() +{ + m_tcpServer = new QTcpServer(); + + if (!m_tcpServer->listen(QHostAddress(QHostAddress::LocalHost), m_port)) { + qFatal("Unable to start the server"); + } + + connect(m_tcpServer, &QTcpServer::newConnection, this, &FakeServer::newConnection); + + exec(); + + qDeleteAll(m_clientSockets); + + delete m_tcpServer; +} + +void FakeServer::started() +{ + // do nothing: this is a dummy slot used by startAndWait() +} + +void FakeServer::setScenario(const QList &scenario) +{ + QMutexLocker locker(&m_mutex); + + m_scenarios.clear(); + m_scenarios << scenario; +} + +void FakeServer::addScenario(const QList &scenario) +{ + QMutexLocker locker(&m_mutex); + + m_scenarios << scenario; +} + +void FakeServer::addScenarioFromFile(const QString &fileName) +{ + QFile file(fileName); + file.open(QFile::ReadOnly); + + QList scenario; + + while (!file.atEnd()) { + scenario << file.readLine().trimmed(); + } + + file.close(); + + addScenario(scenario); +} + +bool FakeServer::isScenarioDone(int scenarioNumber) const +{ + QMutexLocker locker(&m_mutex); + + if (scenarioNumber < m_scenarios.size()) { + return m_scenarios[scenarioNumber].isEmpty(); + } else { + return true; // Non existent hence empty, right? + } +} + +bool FakeServer::isAllScenarioDone() const +{ + QMutexLocker locker(&m_mutex); + + foreach (const QList &scenario, m_scenarios) { + if (!scenario.isEmpty()) { + return false; + } + } + + return true; +} + +void FakeServer::writeServerPart(int scenarioNumber) +{ + QList scenario = m_scenarios[scenarioNumber]; + QTcpSocket *clientSocket = m_clientSockets[scenarioNumber]; + + while (!scenario.isEmpty() && + scenario.first().startsWith("S: ")) { + QByteArray rule = scenario.takeFirst(); + + QByteArray payload = rule.mid(3); + clientSocket->write(payload + "\r\n"); + } + + QByteArray data; + + while (!scenario.isEmpty() && + scenario.first().startsWith("D: ")) { + QByteArray rule = scenario.takeFirst(); + + QByteArray payload = rule.mid(3); + data.append(payload + "\r\n"); + } + + clientSocket->write(QStringLiteral("Content-Length: %1\r\n\r\n").arg(data.length()).toLatin1()); + clientSocket->write(data); + + if (!scenario.isEmpty() && + scenario.first().startsWith("X")) { + scenario.takeFirst(); + clientSocket->close(); + } + + if (!scenario.isEmpty()) { + QVERIFY(scenario.first().startsWith("C: ")); + } + + m_scenarios[scenarioNumber] = scenario; +} + +void FakeServer::readClientPart(int scenarioNumber) +{ + QList scenario = m_scenarios[scenarioNumber]; + QTcpSocket *socket = m_clientSockets[scenarioNumber]; + QByteArray line = socket->readLine(); + QVector header; + + while(line != "\r\n") { + header << line; + line = socket->readLine(); + } + + while (!scenario.isEmpty() && + scenario.first().startsWith("C: ")) { + QByteArray expected = scenario.takeFirst().mid(3) + "\r\n"; + + if (!header.contains(expected)) { + qWarning() << expected << "not found in header"; + QVERIFY(false); + break; + } + } + + if (!scenario.isEmpty()) { + QVERIFY(scenario.first().startsWith("S: ")); + } + + m_scenarios[scenarioNumber] = scenario; +} + +int FakeServer::port() const +{ + return m_port; +} diff --git a/autotests/fakeserver.h b/autotests/fakeserver.h new file mode 100644 index 0000000..9235ec6 --- /dev/null +++ b/autotests/fakeserver.h @@ -0,0 +1,195 @@ +/* + Copyright (C) 2008 Omat Holding B.V. + + Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company + Author: Kevin Ottens + + Copyright (C) 2017 Sandro Kanuß + + 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 FAKESERVER_H +#define FAKESERVER_H + +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QList) + +/** + * Pretends to be an DAV server for the purposes of unit tests. + * + * FakeServer does not really understand the DAV protocol. Instead, + * you give it a script, or scenario, that lists how an DAV session + * exchange should go. When it receives the client parts of the + * scenario, it will respond with the following server parts. + * + * The server can be furnished with several scenarios. The first + * scenario will be played out to the first client that connects, the + * second scenario to the second client connection and so on. + * + * The fake server runs as a separate thread in the same process it + * is started from, and listens for connections (see port() method) on the + * local machine. + * + * Scenarios are in the form of protocol messages, with a tag at the + * start to indicate whether it is message that will be sent by the + * client ("C: ") or a response that should be sent by the server + * ("S: "). Or ("D: ") for the exchanged data. Content-length header is added + * automatically with the corrent length and also the empty line between Header + * and Content. For example: + * @code + * C: GET /item HTTP/1.1 + * S: HTTP/1.0 200 OK + * D: much data + * D: more data + * X + * @endcode + * + * A line starting with X indicates that the connection should be + * closed by the server. This should be the last line in the + * scenario. + + * A typical usage is something like + * @code + * QList scenario; + * scenario << "C: GET /item HTTP/1.1" + * << "S: HTTP/1.0 200 OK" + * << "D: much data" + * << "D: more data" + * << "X"; + * + * FakeServer fakeServer; + * fakeServer.setScenario(scenario); + * fakeServer.startAndWait(); + * + * QUrl url(QStringLiteral("http://localhost/item")); + * url.setPort(fakeServer.port()); + * KDAV::DavUrl davUrl(url, KDAV::CardDav); + * KDAV::DavItem item(davUrl, QString(), QByteArray(), QString()); + * + * auto job = new KDAV::DavItemFetchJob(item); + * job->exec(); + * fakeServer.quit(); + * QVERIFY(fakeServer.isAllScenarioDone()); + * @endcode + */ + +class FakeServer : public QThread +{ + Q_OBJECT + +public: + FakeServer(QObject *parent = nullptr); + ~FakeServer(); + + /** + * Starts the server and waits for it to be ready + * + * You should use this instead of start() to avoid race conditions. + */ + void startAndWait(); + + /** + * Starts the fake server + * + * You should not call this directly. Use start() instead. + * + * @reimp + */ + void run() override; + + /** + * Removes any previously-added scenarios, and adds a new one + * + * After this, there will only be one scenario, and so the fake + * server will only be able to service a single request. More + * scenarios can be added with addScenario, though. + * + * @see addScenario()\n + * addScenarioFromFile() + */ + void setScenario(const QList &scenario); + + /** + * Adds a new scenario + * + * Note that scenarios will be used in the order that clients + * connect. If this is the 5th scenario that has been added + * (bearing in mind that setScenario() resets the scenario + * count), it will be used to service the 5th client that + * connects. + * + * @see addScenarioFromFile() + * + * @param scenario the scenario as a list of messages + */ + void addScenario(const QList &scenario); + /** + * Adds a new scenario from a local file + * + * Note that scenarios will be used in the order that clients + * connect. If this is the 5th scenario that has been added + * (bearing in mind that setScenario() resets the scenario + * count), it will be used to service the 5th client that + * connects. + * + * @see addScenario() + * + * @param fileName the name of the file that contains the + * scenario; it will be split at line + * boundaries, and excess whitespace will + * be trimmed from the start and end of lines + */ + void addScenarioFromFile(const QString &fileName); + + /** + * Checks whether a particular scenario has completed + * + * @param scenarioNumber the number of the scenario to check, + * in order of addition/client connection + */ + bool isScenarioDone(int scenarioNumber) const; + /** + * Whether all the scenarios that were added to the fake + * server have been completed. + */ + bool isAllScenarioDone() const; + + /** + * Returns the port where the fake server is listening. + */ + int port() const; + +private Q_SLOTS: + void newConnection(); + void dataAvailable(); + void started(); + +private: + void writeServerPart(int scenarioNumber); + void readClientPart(int scenarioNumber); + + QList< QList > m_scenarios; + QTcpServer *m_tcpServer; + mutable QMutex m_mutex; + QList m_clientSockets; + int m_port; +}; + +#endif diff --git a/kdav.categories b/kdav.categories new file mode 100644 index 0000000..d9cf88e --- /dev/null +++ b/kdav.categories @@ -0,0 +1 @@ +org.kde.pim.kdav KDAV diff --git a/metainfo.yaml b/metainfo.yaml new file mode 100644 index 0000000..46f73c2 --- /dev/null +++ b/metainfo.yaml @@ -0,0 +1,17 @@ +maintainer: +description: The KDav library +type: functional +platforms: + - name: All +portingAid: false +deprecated: false +release: false +libraries: + - qmake: PimKDAV + cmake: "KPimKDAV" + cmakename: KPimKDAV + +public_lib: true +group: kdepim +platforms: + - name: Linux diff --git a/po/ar/libkdav.po b/po/ar/libkdav.po new file mode 100644 index 0000000..4dc6214 --- /dev/null +++ b/po/ar/libkdav.po @@ -0,0 +1,158 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# Safa Alfulaij , 2018. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2018-01-31 22:49+0300\n" +"Last-Translator: Safa Alfulaij \n" +"Language-Team: Arabic \n" +"Language: ar\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "اسم المستخدم/كلمة السّرّ غير صالحة" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "النّفاذ ممنوع" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "لم يُعثر على المورد" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "خطأ HTTP" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"حدثت مشكلة بالطّلب.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "لا يدعم ميفاق التّجميعات MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "واجه الخادوم خطأً منعه من إكمال الطّلب: %1 ‏(%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"حدثت مشكلة بالطّلب. لم تُحذف التّجميعة من الخادوم.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "ردود غير صالحة من السّند" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "خطأ في ضبط التّركيز على XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "أرسل تنفيذ DAV استعلام XQuery غير صالح" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"حدثت مشكلة بالطّلب. لم تُعدّل التّجميعة في الخادوم.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "لا خصائص لتغييرها أو إزالتها" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "حدث خطأ أثناء تعديل الخصائص" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"أعاد الخادوم معلومات أكثر:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"حدثت مشكلة بالطّلب. لم يُنشأ العنصر في الخادوم.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"حدثت مشكلة بالطّلب. لم يُحذف العنصر من الخادوم.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"حدثت مشكلة بالطّلب. لم يُعدّل العنصر في الخادوم.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "حدثت مشكلة بالطّلب." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "حدثت مشكلة بالطّلب. أنواع MIME المطلوبة غير مدعومة." diff --git a/po/ast/libkdav.po b/po/ast/libkdav.po new file mode 100644 index 0000000..9e1ff68 --- /dev/null +++ b/po/ast/libkdav.po @@ -0,0 +1,142 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# enolp , 2018. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2018-01-17 17:59+0100\n" +"Last-Translator: enolp \n" +"Language-Team: Asturian <>\n" +"Language: ast\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Nun s'alcontró'l recursu" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "" + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" diff --git a/po/ca/libkdav.po b/po/ca/libkdav.po new file mode 100644 index 0000000..e488aef --- /dev/null +++ b/po/ca/libkdav.po @@ -0,0 +1,171 @@ +# Translation of libkdav.po to Catalan +# Copyright (C) 2009-2017 This_file_is_part_of_KDE +# This file is distributed under the license LGPL version 2.1 or +# version 3 or later versions approved by the membership of KDE e.V. +# +# Manuel Tortosa Moreno , 2009, 2010. +# Josep Ma. Ferrer , 2010, 2011, 2012, 2015, 2017. +# Antoni Bella Pérez , 2012, 2013, 2014, 2015, 2016, 2017. +msgid "" +msgstr "" +"Project-Id-Version: libkdav\n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2017-12-12 12:17+0100\n" +"Last-Translator: Antoni Bella Pérez \n" +"Language-Team: Catalan \n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Lokalize 2.0\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Accelerator-Marker: &\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Nom d'usuari o contrasenya no vàlids" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Accés prohibit" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "No s'ha trobat el recurs" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "Error d'HTTP" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"S'ha produït un problema amb la sol·licitud.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "El protocol per a la col·lecció no permet MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"El servidor ha trobat un error que impedeix completar la vostra sol·licitud: " +"%1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"S'ha produït un problema amb la sol·licitud. La col·lecció no ha estat " +"eliminada del servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Respostes no vàlides del dorsal" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Error en establir el focus a XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "XQuery no vàlida enviada per la implementació DAV" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"S'ha produït un problema amb la sol·licitud. La col·lecció no ha estat " +"modificada en el servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "No hi ha cap propietat a canviar o eliminar" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "S'ha produït un error en modificar les propietats" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"El servidor ha retornat més informació:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"S'ha produït un problema amb la sol·licitud. L'element no ha estat creat en " +"el servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"S'ha produït un problema amb la sol·licitud. L'element no ha estat eliminat " +"del servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"S'ha produït un problema amb la sol·licitud. L'element no ha estat modificat " +"en el servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "S'ha produït un problema amb la sol·licitud." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" +"S'ha produït un problema amb la sol·licitud. Els tipus MIME requerits no " +"estan implementats." diff --git a/po/ca@valencia/libkdav.po b/po/ca@valencia/libkdav.po new file mode 100644 index 0000000..d6d1441 --- /dev/null +++ b/po/ca@valencia/libkdav.po @@ -0,0 +1,171 @@ +# Translation of libkdav.po to Catalan (Valencian) +# Copyright (C) 2009-2017 This_file_is_part_of_KDE +# This file is distributed under the license LGPL version 2.1 or +# version 3 or later versions approved by the membership of KDE e.V. +# +# Manuel Tortosa Moreno , 2009, 2010. +# Josep Ma. Ferrer , 2010, 2011, 2012, 2015, 2017. +# Antoni Bella Pérez , 2012, 2013, 2014, 2015, 2016, 2017. +msgid "" +msgstr "" +"Project-Id-Version: libkdav\n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2017-12-12 12:17+0100\n" +"Last-Translator: Antoni Bella Pérez \n" +"Language-Team: Catalan \n" +"Language: ca@valencia\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Lokalize 2.0\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Accelerator-Marker: &\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Nom d'usuari o contrasenya no vàlids" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Accés prohibit" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "No s'ha trobat el recurs" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "Error d'HTTP" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"S'ha produït un problema amb la sol·licitud.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "El protocol per a la col·lecció no permet MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"El servidor ha trobat un error que impedeix completar la vostra sol·licitud: " +"%1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"S'ha produït un problema amb la sol·licitud. La col·lecció no ha estat " +"eliminada del servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Respostes no vàlides del dorsal" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Error en establir el focus a XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "XQuery no vàlida enviada per la implementació DAV" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"S'ha produït un problema amb la sol·licitud. La col·lecció no ha estat " +"modificada en el servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "No hi ha cap propietat a canviar o eliminar" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "S'ha produït un error en modificar les propietats" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"El servidor ha retornat més informació:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"S'ha produït un problema amb la sol·licitud. L'element no ha estat creat en " +"el servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"S'ha produït un problema amb la sol·licitud. L'element no ha estat eliminat " +"del servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"S'ha produït un problema amb la sol·licitud. L'element no ha estat modificat " +"en el servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "S'ha produït un problema amb la sol·licitud." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" +"S'ha produït un problema amb la sol·licitud. Els tipus MIME requerits no " +"estan implementats." diff --git a/po/cs/libkdav.po b/po/cs/libkdav.po new file mode 100644 index 0000000..8c79cd1 --- /dev/null +++ b/po/cs/libkdav.po @@ -0,0 +1,146 @@ +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Vít Pelčák , 2010, 2011, 2013, 2016, 2017, 2018. +# Tomáš Chvátal , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2017-02-27 13:49+0100\n" +"Last-Translator: Vít Pelčák \n" +"Language-Team: Czech \n" +"Language: cs\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Neplatné uživatelské jméno nebo heslo" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Přístup zakázán" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Zdroj nebyl nalezen" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "Chyba HTTP" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"Server navrátil více informací:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "V požadavku nastal problém." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" diff --git a/po/de/libkdav.po b/po/de/libkdav.po new file mode 100644 index 0000000..f18eb49 --- /dev/null +++ b/po/de/libkdav.po @@ -0,0 +1,167 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# Burkhard Lück , 2017, 2018. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2018-02-27 13:42+0100\n" +"Last-Translator: Burkhard Lück \n" +"Language-Team: German \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Ungültiger Benutzername/Passwort" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Zugriff unzulässig" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Die Ressource wurde nicht gefunden" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "HTTP-Fehler" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Bei der Abfrage ist ein Problem aufgetreten\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "Das Protokoll für die Sammlung unterstützt MULTIGET nicht." + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"Der Server hat einen Fehler festgestellt, der die Fertigstellung Ihrer " +"Abfrage verhindert: %1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Bei der Abfrage ist ein Problem aufgetreten. Die Sammlung wurde nicht vom " +"Server gelöscht.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Ungültige Antwort des Treibers" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Fehler beim Bestimmen des Fokus für XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "DAV-Implementation hat ungültige XQuery übermittelt" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"Bei der Abfrage ist ein Problem aufgetreten. Die Sammlung wurde auf dem " +"Server nicht verändert.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "" +"Keine Eigenschaften vorhanden, die verändert oder gelöscht werden können." + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Problem beim Anpassen der Eigenschaften" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"Der Server hat weitere Informationen zurückgegeben:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Bei der Abfrage ist ein Problem aufgetreten. Der Eintrag wurde auf dem " +"Server nicht erstellt\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Bei der Abfrage ist ein Problem aufgetreten. Der Eintrag wurde nicht vom " +"Server gelöscht\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Bei der Abfrage ist ein Problem aufgetreten. Der Eintrag wurde auf dem " +"Server nicht verändert\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "Bei der Abfrage ist ein Problem aufgetreten" + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" +"Bei der Abfrage ist ein Problem aufgetreten. Die angeforderten MIME-Typen " +"werden nicht unterstützt." diff --git a/po/en_GB/libkdav.po b/po/en_GB/libkdav.po new file mode 100644 index 0000000..deecf58 --- /dev/null +++ b/po/en_GB/libkdav.po @@ -0,0 +1,166 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# Steve Allewell , 2017. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2017-12-22 15:41+0000\n" +"Last-Translator: Steve Allewell \n" +"Language-Team: British English \n" +"Language: en_GB\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Invalid username/password" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Access forbidden" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Resource not found" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "HTTP error" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"There was a problem with the request.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "Protocol for the collection does not support MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Invalid responses from backend" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Error setting focus for XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "Invalid XQuery submitted by DAV implementation" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "No properties to change or remove" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "There was an error when modifying the properties" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"The server returned more information:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "There was a problem with the request." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" +"There was a problem with the request. The requested MIME types are not " +"supported." diff --git a/po/es/libkdav.po b/po/es/libkdav.po new file mode 100644 index 0000000..bc9f7e0 --- /dev/null +++ b/po/es/libkdav.po @@ -0,0 +1,166 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2017-12-14 09:10+0100\n" +"Last-Translator: Javier Viñal \n" +"Language-Team: Spanish \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.0.3\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Usuario/contraseña inválidos" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Acceso prohíbido" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Recurso no encontrado" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "Error HTTP" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Ha habido un problema con la petición.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "El protocolo para la colección no implementa MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"El servidor encontró un error que le impide completar la petición: %1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Ha habido un problema con la petición. La colección no se ha borrado del " +"servidor.\n" +"%1 (%2)" + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Respuesta inválida desde el motor" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Error al establecer el foco para XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "XQuery inválido enviado por la implementación de DAV" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"Ha habido un problema con la petición. La colección no se ha modificado del " +"servidor.\n" +"%1 (%2)" + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Ninguna propiedad que cambiar o eliminar" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Ha habido un error al modificar las propiedades" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"El servidor ha devuelto más información:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Ha habido un problema con la petición. El elemento no se ha creado en el " +"servidor.\n" +"%1 (%2)" + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Ha habido un problema con la petición. El elemento no se ha borrado desde el " +"servidor.\n" +"%1 (%2)" + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Ha habido un problema con la petición. El elemento no se ha modificado en el " +"servidor.\n" +"%1 (%2)" + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "Ha habido un problema con la petición." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" +"Ha habido un problema con la petición. El tipo MIME solicitado no está " +"implementado." diff --git a/po/fi/libkdav.po b/po/fi/libkdav.po new file mode 100644 index 0000000..b80fed1 --- /dev/null +++ b/po/fi/libkdav.po @@ -0,0 +1,164 @@ +# Copyright © 2011, 2012 This_file_is_part_of_KDE +# This file is distributed under the same license as the kdepim-runtime package. +# Tommi Nieminen , 2011, 2012, 2013, 2015, 2016, 2017. +# Lasse Liehu , 2012, 2013, 2014, 2015. +# Jiri Grönroos , 2012. +# +# KDE Finnish translation sprint participants: +# Author: Artnay +# Author: Lliehu +msgid "" +msgstr "" +"Project-Id-Version: akonadi_davgroupware_resource\n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2017-12-25 20:33+0200\n" +"Last-Translator: Tommi Nieminen \n" +"Language-Team: Finnish \n" +"Language: fi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-POT-Import-Date: 2012-12-01 22:24:46+0000\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Virheellinen käyttäjätunnus tai salasana" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Saanti estetty" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Resurssia ei löydy" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "HTTP-virhe" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Pyynnössä oli ongelmia.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "Kokoelman yhteyskäytäntö ei tue MULTIGETiä" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"Palvelin kohtasi virheen, joka esti sitä toteuttamasta pyyntöäsi: %1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Pyynnössä oli ongelmia eikä kokoelmaa poistettu palvelimelta.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Taustaosa vastaa virheellisesti" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Virhe asetettaessa XQueryn kohdistusta" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "DAV-toteutus lähetti virheellisen XQueryn" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"Pyynnössä oli ongelmia eikä kokoelmaa muutettu palvelimella.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Ei muutettavia tai poistettavia ominaisuuksia" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Ominaisuuksia muutettaessa sattui virhe" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"Palvelin palautti lisätietoa:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Pyynnössä oli ongelmia eikä merkintää luotu palvelimeen.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Pyynnössä oli ongelmia eikä merkintää poistettu palvelimelta.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Pyynnössä oli ongelmia eikä merkintää muutettu palvelimella.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "Pyynnössä oli ongelmia." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "Pyynnössä oli ongelmia: pyydettyjä MIME-tyyppejä ei tueta." diff --git a/po/fr/libkdav.po b/po/fr/libkdav.po new file mode 100644 index 0000000..a92c05f --- /dev/null +++ b/po/fr/libkdav.po @@ -0,0 +1,175 @@ +# translation of akonadi_davgroupware_resource.po to Français +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# Guillaume Pujol , 2009, 2010. +# Geoffray Levasseur , 2012, 2013, 2014. +# xavier , 2013. +# Joëlle Cornavin , 2011. +# Sebastien Renard , 2013. +# Vincent Pinon , 2017, 2018. +# +msgid "" +msgstr "" +"Project-Id-Version: akonadi_davgroupware_resource\n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2018-01-11 17:51+0100\n" +"Last-Translator: Vincent Pinon \n" +"Language-Team: French \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Lokalize 2.0\n" +"X-Environment: kde\n" +"X-Accelerator-Marker: &\n" +"X-Text-Markup: kde4\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Nom d'utilisateur ou mot de passe non valable" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Accès refusé" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Impossible de trouver la ressource" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "Erreur « HTTP »" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Il y a eu un problème avec la requête.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "Le protocole pour la collection ne prend pas en charge MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"Le serveur a rencontré une erreur l'empêchant de mener à bien votre " +"requête : %1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Il y a eu un problème avec la requête. La collection n'a pas été supprimée " +"du serveur.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Réponses non valables provenant du moteur" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Problème lors de la définition du focus pour « XQuery »" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "« XQuery » non valable soumise par l'implémentation de DAV" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"Il y a eu un problème avec la requête. La collection n'a pas été modifié sur " +"le serveur.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Aucune propriété à modifier ou supprimer" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Une erreur est survenue lors de la modification des propriétés" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"Le serveur a retourné plus d'informations :\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Il y a eu un problème avec la requête. L'élément n'a pas été créé sur le " +"serveur.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Il y a eu un problème avec la requête. L'élément n'a pas été supprimé du " +"serveur.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Il y a eu un problème avec la requête. L'élément n'a pas été modifié sur le " +"serveur.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "Il y a eu un problème avec la requête." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" +"Il y a eu un problème avec la requête. Les types MIME demandés ne sont pas " +"pris en charge." diff --git a/po/gl/libkdav.po b/po/gl/libkdav.po new file mode 100644 index 0000000..7b5f130 --- /dev/null +++ b/po/gl/libkdav.po @@ -0,0 +1,162 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# Adrian Chaves , 2018. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2018-04-22 13:29+0100\n" +"Last-Translator: Adrian Chaves \n" +"Language-Team: Galician \n" +"Language: gl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "O nome de usuario ou o contrasinal son incorrectos" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Denegouse o acceso" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Non se atopou o recurso." + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "Erro de HTTP" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Houbo un problema coa solicitude.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "O protocolo da colección non admite MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"O servidor atopouse cun erro que evitou que realizase a súa solicitude: %1 " +"(%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Produciuse un problema coa solicitude. A colección non se eliminou do " +"servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "A infraestrutura remitiu respostas incorrectas" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Produciuse un erro ao configurar o foco de XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "A realización de DAV enviou unha XQuery incorrecta" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"Houbo un problema coa solicitude. A colección non se modificou no servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Non hai propiedades que cambiar ou retirar" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Produciuse un erro ao modificar as propiedades." + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"O servidor devolveu máis información:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Houbo un problema coa solicitude. O elemento non se creou no servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Houbo un problema coa solicitude. O elemento non se eliminou do servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Houbo un problema coa solicitude. O elemento non se modificou no servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "Houbo un problema coa solicitude." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" +"Houbo un problema coa solicitude. O tipos MIME solicitados non son " +"compatíbeis." diff --git a/po/it/libkdav.po b/po/it/libkdav.po new file mode 100644 index 0000000..346a52e --- /dev/null +++ b/po/it/libkdav.po @@ -0,0 +1,158 @@ +# translation of akonadi_davgroupware_resource.po to Italian +# +# Vincenzo Reale , 2009, 2010. +# Nicola Ruggero , 2010, 2011. +# Luigi Toscano , 2012, 2013, 2014, 2015, 2016, 2017. +msgid "" +msgstr "" +"Project-Id-Version: akonadi_davgroupware_resource\n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2017-12-14 21:04+0100\n" +"Last-Translator: Luigi Toscano \n" +"Language-Team: Italian \n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Username o password non validi" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Accesso vietato" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Risorsa non trovata" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "Errore HTTP" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Problema con la richiesta.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "Il protocollo della collezione non supporta MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "Errore nel server. Impossibile completare la richiesta: %1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Problema con la richiesta. La collezione non è stata eliminata dal server.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Risposta non valida dal backend" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Errore impostando il fuoco su XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "XQuery non valida inviata dall'implementazione DAV" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"Problema con la richiesta. La collezione non è stata modificata sul server.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Nessuna proprietà da cambiare o rimuovere" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Errore nella modifica delle proprietà" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"Il server ha fornito ulteriori informazioni:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Problema con la richiesta. L'elemento non è stato creato sul server.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Problema con la richiesta. L'elemento non è stato eliminato dal server.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Problema con la richiesta. L'elemento non è stato modificato sul server.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "Problema con la richiesta." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "Problema con la richiesta. I tipi MIME richiesti non sono supportati." diff --git a/po/ja/libkdav.po b/po/ja/libkdav.po new file mode 100644 index 0000000..3e71dbd --- /dev/null +++ b/po/ja/libkdav.po @@ -0,0 +1,139 @@ +msgid "" +msgstr "" +"Project-Id-Version: akonadi_davcalendar_resource\n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2009-11-04 20:48+0900\n" +"Last-Translator: Japanese KDE translation team \n" +"Language-Team: Japanese \n" +"Language: ja\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Accelerator-Marker: &\n" +"X-Text-Markup: kde4\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "" + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" diff --git a/po/ko/libkdav.po b/po/ko/libkdav.po new file mode 100644 index 0000000..4c166fe --- /dev/null +++ b/po/ko/libkdav.po @@ -0,0 +1,157 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# Shinjo Park , 2013, 2014, 2015, 2016, 2017, 2018. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2018-08-15 12:26+0100\n" +"Last-Translator: Shinjo Park \n" +"Language-Team: Korean \n" +"Language: ko\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "잘못된 사용자 이름 및 암호" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "접근 거부됨" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "자원을 찾을 수 없음" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "HTTP 오류" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"요청에 문제가 있습니다.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "모음집에 대한 프로토콜이 MULTIGET을 지원하지 않음" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "서버에서 요청을 처리할 수 없는 오류가 발생했습니다: %1(%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"요청에 문제가 있습니다. 서버에서 모음집이 삭제되지 않았습니다:\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "백엔드에서 잘못된 응답이 돌아옴" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "XQuery 초점 설정 오류" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "DAV 구현에서 잘못된 XQuery를 보냄" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"요청에 문제가 있습니다. 서버에서 모음집이 수정되지 않았습니다:\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "변경하거나 삭제할 속성 없음" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "속성을 수정하는 중 오류가 발생함" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"서버에서 부가 정보를 돌려 줌:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"요청에 문제가 있습니다. 서버에서 모음집이 생성되지 않았습니다:\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"요청에 문제가 있습니다. 서버에서 항목이 삭제되지 않았습니다:\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"요청에 문제가 있습니다. 서버에서 항목이 수정되지 않았습니다:\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "요청에 문제가 있습니다." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "요청에 문제가 있습니다. 지정한 MIME 형식을 지원하지 않습니다." diff --git a/po/nb/libkdav.po b/po/nb/libkdav.po new file mode 100644 index 0000000..e094cb3 --- /dev/null +++ b/po/nb/libkdav.po @@ -0,0 +1,158 @@ +# Translation of libkdav to Norwegian BokmÃ¥l +# +# Bjørn Steensrud , 2010, 2011, 2013, 2014. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2014-09-19 22:47+0200\n" +"Last-Translator: Bjørn Steensrud \n" +"Language-Team: Norwegian BokmÃ¥l \n" +"Language: nb\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Lokalize 1.5\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Environment: kde\n" +"X-Accelerator-Marker: &\n" +"X-Text-Markup: kde4\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Ugyldig brukernavn/passord" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Adgang forbudt" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Fant ikke ressurs" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "HTTP-feil" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Det var et problem med forespørselen.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "Protokollen for samlingen støtter ikke MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Det var et problem med forespørselen. Samlingen er ikke slettet fra " +"tjeneren.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Ugyldige svar fra bakgrunnsmotor" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Feil ved fokussetting for XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "Ugyldig XQuery sendt inn av DAV-implementasjon" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Ingen egenskaper som skal endres eller fjernes" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Det oppsto en feil ved endring av egenskapene" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Det var et problem med forespørselen. Elementet er ikke opprettet pÃ¥ " +"tjeneren.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Det var et problem med forespørselen. Elementet er ikke slettet fra " +"tjeneren.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Det var et problem med forespørselen. Elementet ble ikke endret pÃ¥ " +"tjeneren.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "" + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" diff --git a/po/nl/libkdav.po b/po/nl/libkdav.po new file mode 100644 index 0000000..6f1ac8e --- /dev/null +++ b/po/nl/libkdav.po @@ -0,0 +1,166 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# Freek de Kruijf , 2017. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2017-12-12 22:49+0100\n" +"Last-Translator: Freek de Kruijf \n" +"Language-Team: Dutch \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Ongeldige gebruikersnaam/wachtwoord" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Toegang verboden" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Hulpbron niet gevonden" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "HTTP-fout" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Er was een probleem met het verzoek.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "Protocol voor de verzameling ondersteunt geen MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"De server kwam een fout tegen, waardoor deze uw verzoek niet kon afhandelen: " +"%1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Er was een probleem met het verzoek. De verzameling is niet van de server " +"verwijderd.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Ongeldige antwoorden van backend" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Fout bij het instellen van de focus voor XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "Ongeldige Xquery ingediend door DAV-implementatie" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"Er was een probleem met het verzoek. De verzameling is niet gewijzigd op de " +"server.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Er zijn geen eigenschappen te wijzigen of te verwijderen" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Er trad een fout op bij het wijzigen van de eigenschappen" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"De server gaf meer informatie terug:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Er was een probleem met het verzoek. Het item is niet aangemaakt op de " +"server\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Er was een probleem met het verzoek. Het item is niet van de server " +"verwijderd.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Er was een probleem met het verzoek. Het item is niet gewijzigd op de " +"server.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "Er was een probleem met het verzoek." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" +"Er was een probleem met het verzoek. De gevraagde MIME-typen worden niet " +"ondersteund." diff --git a/po/pl/libkdav.po b/po/pl/libkdav.po new file mode 100644 index 0000000..db441da --- /dev/null +++ b/po/pl/libkdav.po @@ -0,0 +1,159 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# Łukasz Wojniłowicz , 2018. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2018-01-28 06:08+0100\n" +"Last-Translator: Łukasz Wojniłowicz \n" +"Language-Team: Polish \n" +"Language: pl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2);\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Nieprawidłowa nazwa użytkownika/hasło" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Dostęp wzbroniony" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Nie znaleziono zasobu" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "Błąd HTTP" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Wystąpił problem z żądaniem\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "Protokół dla zbioru nie obsługuje MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"Serwer napotkał błąd który uniemożliwił spełnienie twojego żądania: %1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Wystąpił problem z żądaniem. Zbiór nie został usunięty z serwera.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Nieprawidłowa odpowiedź z modułu obsługi" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Błąd ustawiania ostrości dla XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "Nieprawidłowe XQuery wykonane przez DAV" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"Wystąpił problem z żądaniem. Zbiór nie został zmieniony na serwerze.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Nie ma właściwości, aby zmienić lub usunąć" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Wystąpił błąd w trakcje zmiany właściwości" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"Serwer zwrócił więcej informacji:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Wystąpił problem z żądaniem. Element nie został stworzona na serwerze.\n" +" %1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Wystąpił problem z żądaniem. Element nie został usunięty na serwerze.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Wystąpił problem z żądaniem. Element nie został zmieniony na serwerze.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "Wystąpił problem z żądaniem." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "Wystąpił problem z żądaniem. Żądane typy MIME nie są obsługiwane." diff --git a/po/pt/libkdav.po b/po/pt/libkdav.po new file mode 100644 index 0000000..12eed95 --- /dev/null +++ b/po/pt/libkdav.po @@ -0,0 +1,156 @@ +msgid "" +msgstr "" +"Project-Id-Version: libkdav\n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2017-12-04 11:32+0000\n" +"Last-Translator: José Nuno Coelho Pires \n" +"Language-Team: Portuguese \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-POFile-SpellExtra: XQuery MULTIGET DAV\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Utilizador/senha inválidos" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Acesso proibido" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Recurso não encontrado" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "Erro de HTTP" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Ocorreu um problema com o pedido.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "O protocolo da colecção não suporta o MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"O servidor encontrou um erro que o impediu de terminar o seu pedido: %1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Ocorreu um problema com o pedido. A colecção não foi apagada no servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Foi obtida uma resposta inválida da infra-estrutura" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Ocorreu um erro na atribuição do XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "Foi enviado um pedido de XQuery inválido pela implementação do DAV" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"Ocorreu um problema com o pedido. A colecção não foi modificada no " +"servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Não existem propriedades para alterar ou remover" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Ocorreu um erro ao modificar as propriedades" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"O servidor devolveu mais informações:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Ocorreu um problema com o pedido - O item não foi criado no servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Ocorreu um problema com o pedido - o item não foi apagado no servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Ocorreu um problema com o pedido. O item não foi modificado no servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "Ocorreu um problema com o pedido." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" +"Ocorreu um problema com o pedido. Os tipos MIME pedidos não são suportados." diff --git a/po/pt_BR/libkdav.po b/po/pt_BR/libkdav.po new file mode 100644 index 0000000..6a25684 --- /dev/null +++ b/po/pt_BR/libkdav.po @@ -0,0 +1,166 @@ +# Translation of libkdav.po to Brazilian Portuguese +# Copyright (C) 2018 This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# André Marcelo Alvarenga , 2018. +msgid "" +msgstr "" +"Project-Id-Version: libkdav\n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2018-07-24 23:51-0300\n" +"Last-Translator: André Marcelo Alvarenga \n" +"Language-Team: Brazilian Portuguese \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Usuário/senha inválidos" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Acesso proibido" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Recurso não encontrado" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "Erro de HTTP" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Ocorreu um problema com a solicitação.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "O protocolo da coleção não tem suporte ao MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"O servidor encontrou um erro que o impediu de concluir a sua solicitação: %1 " +"(%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Ocorreu um problema com a solicitação. A coleção não foi excluída no " +"servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Foi obtida uma resposta inválida da infraestrutura" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Ocorreu um erro na configuração do XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "" +"Foi enviada uma solicitação de XQuery inválida pela implementação do DAV" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"Ocorreu um problema com a solicitação. A coleção não foi modificada no " +"servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Nenhuma propriedade para alterar ou remover" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Ocorreu um erro ao modificar as propriedades" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"O servidor retornou mais informações:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Ocorreu um problema com a solicitação. O item não foi criado no servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Ocorreu um problema com a solicitação. O item não foi excluído no servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Ocorreu um problema com a solicitação. O item não foi modificado no " +"servidor.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "Ocorreu um problema com a solicitação." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" +"Ocorreu um problema com a solicitação. Não há suporte para os tipos MIME " +"solicitados." diff --git a/po/ru/libkdav.po b/po/ru/libkdav.po new file mode 100644 index 0000000..7cec0c6 --- /dev/null +++ b/po/ru/libkdav.po @@ -0,0 +1,165 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# Alexander Potashev , 2017. +# Alexander Yavorsky , 2018. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2018-07-16 21:52+0300\n" +"Last-Translator: Alexander Yavorsky \n" +"Language-Team: Russian \n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n" +"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Неправильное имя пользователя или пароль" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Доступ запрещён" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Ресурс не найден" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "Ошибка HTTP" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"При выполнении запроса произошла ошибка.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "Протокол для этой коллекции не поддерживает MULTIGET." + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"На сервере произошла ошибка, из-за чего он не смог завершить выполнение " +"запроса: %1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"При выполнении запроса произошла ошибка. Коллекция не была удалена с " +"сервера.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Неверный ответ сервера" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Ошибка установки фокуса для XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "Неверный XQuery, представленный реализацией DAV" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"При выполнении запроса произошла ошибка. Коллекция не была изменена на " +"сервере.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Нет свойств для изменения или удаления" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "При изменении свойств произошла ошибка" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"Сервер вернул расширенные сведения:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"При выполнении запроса произошла ошибка. Объект не был создан на сервере.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"При выполнении запроса произошла ошибка. Объект не был удалён с сервера.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"При выполнении запроса произошла ошибка. Объект не был изменён на сервере.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "При выполнении запроса произошла ошибка." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" +"При выполнении запроса произошла ошибка. Запрошенные типы MIME не " +"поддерживаются." diff --git a/po/sk/libkdav.po b/po/sk/libkdav.po new file mode 100644 index 0000000..d3fc91b --- /dev/null +++ b/po/sk/libkdav.po @@ -0,0 +1,159 @@ +# translation of libkdav.po to Slovak +# Roman Paholík , 2017. +msgid "" +msgstr "" +"Project-Id-Version: libkdav\n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2017-12-03 21:41+0100\n" +"Last-Translator: Roman Paholik \n" +"Language-Team: Slovak \n" +"Language: sk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Lokalize 2.0\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Neplatné užívateľské meno/heslo" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Prístup zamietnutý" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Zdroj nenájdený" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "HTTP chyba" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Bol problém s požiadavkou.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "Protokol pre kolekciu nepodporuje MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"Nastala chyba servera, ktorá znemožnila vykonaÅ¥ vaÅ¡u požiadavku: %1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Nastala chyba s požiadavkou. Kolekcia nebola vymazaná zo servera.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Neplatná odpoveď z backendu" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Chyba nastavenia zamerania na XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "Neplatná XQuery poslaná implementáciou DAV" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"Nastala chyba s požiadavkou. Položka nebola zmenená na serveri.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Žiadne vlastnosti na zmenu alebo odstránenie" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Nastala chyba počas zmeny vlastností" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"Server vrátil viac informácií:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Nastala chyba s požiadavkou. Položka nebola vytvorená na serveri.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Nastala chyba s požiadavkou. Položka nebola vymazaná zo servera.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Nastala chyba s požiadavkou. Položka nebola zmenená na serveri.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "Bol problém s požiadavkou." + +#: common/daverror.cpp:144 +#, fuzzy, kde-format +#| msgid "" +#| "There was a problem with the request. The requested mimetypes are not " +#| "supported." +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "Nastal problém s požiadavkou. Požadované mime typy nie sú podporované." diff --git a/po/sr/libkdav.po b/po/sr/libkdav.po new file mode 100644 index 0000000..10eb3c9 --- /dev/null +++ b/po/sr/libkdav.po @@ -0,0 +1,159 @@ +# Translation of libkdav.po into Serbian. +# Chusslove Illich , 2011, 2013, 2014, 2015, 2016, 2017. +msgid "" +msgstr "" +"Project-Id-Version: libkdav\n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2017-12-17 18:01+0100\n" +"Last-Translator: Chusslove Illich \n" +"Language-Team: Serbian \n" +"Language: sr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n" +"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Accelerator-Marker: &\n" +"X-Text-Markup: kde4\n" +"X-Environment: kde\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Лоше корисничко име или лозинка" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Приступ забрањен" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Ресурс није нађен" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "ХТТП грешка" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Дошло је до проблема са захтевом.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "Протокол за збирке не подржава MULTIGET." + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"Сервер је наишао на грешку која га је спречила да доврши ваш захтев: %1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Дошло је до проблема са захтевом. Збирка није обрисана са сервера.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Неисправни одговори из позадине." + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Грешка у постављању фокуса за икс‑упит." + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "Изведба ДАВ‑а предала је лош икс‑упит." + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"Дошло је до проблема са захтевом. Збирка није измењена на серверу.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Нема својстава за мењање или уклањање." + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Грешка при мењању својстава." + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"Сервер је вратио још података:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Дошло је до проблема са захтевом. Ставка није створена на серверу.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Дошло је до проблема са захтевом. Ставка није обрисана са сервера.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Дошло је до проблема са захтевом, ставка није измењена на серверу.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "Дошло је до проблема са захтевом." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "Дошло је до проблема са захтевом. Тражени МИМЕ типови нису подржани." diff --git a/po/sv/libkdav.po b/po/sv/libkdav.po new file mode 100644 index 0000000..14a5a1b --- /dev/null +++ b/po/sv/libkdav.po @@ -0,0 +1,161 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# Stefan Asserhäll , 2017. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2017-12-12 19:37+0100\n" +"Last-Translator: Stefan Asserhäll \n" +"Language-Team: Swedish \n" +"Language: sv\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Ogiltigt användarnamn eller lösenord" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Åtkomst nekas" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Resurs hittades inte" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "HTTP-fel" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Ett problem uppstod med begäran.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "Protokoll för samlingen stöder inte MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"Servern rÃ¥kade ut för ett fel som förhindrade den att färdigställa din " +"begäran: %1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Ett problem uppstod med begäran. Samlingen har inte tagits bort frÃ¥n " +"servern.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Felaktigt svar frÃ¥n gränssnitt" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Fel när fokus skulle ställas in för XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "Felaktig XQuery skickades av DAV-implementering" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"Ett problem uppstod med begäran. Samlingen har inte ändrats pÃ¥ servern.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Inga egenskaper att ändra eller ta bort" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Ett fel uppstod när egenskaperna skulle ändras" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"Servern returnerade mer information:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Ett problem uppstod med begäran. Objektet har inte skapats pÃ¥ servern.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Ett problem uppstod med begäran. Objektet har inte tagits bort frÃ¥n " +"servern.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Ett problem uppstod med begäran. Objektet ändrades inte pÃ¥ servern.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "Ett problem uppstod med begäran." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "Ett problem uppstod med begäran. De begärda Mime-typerna stöds inte." diff --git a/po/uk/libkdav.po b/po/uk/libkdav.po new file mode 100644 index 0000000..9337607 --- /dev/null +++ b/po/uk/libkdav.po @@ -0,0 +1,169 @@ +# Translation of libkdav.po to Ukrainian +# Copyright (C) 2017 This_file_is_part_of_KDE +# This file is distributed under the license LGPL version 2.1 or +# version 3 or later versions approved by the membership of KDE e.V. +# +# Yuri Chornoivan , 2017. +msgid "" +msgstr "" +"Project-Id-Version: libkdav\n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2017-12-12 08:38+0200\n" +"Last-Translator: Yuri Chornoivan \n" +"Language-Team: Ukrainian \n" +"Language: uk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n" +"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Lokalize 2.0\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "Некоректне ім’я користувача чи пароль" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "Доступ заборонено" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "Ресурс не знайдено" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "Помилка HTTP" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "" +"Під час виконання запиту виникла проблема.\n" +"%1 (%2)." + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "У протоколі для роботи з цією збіркою не передбачено команди MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "" +"На сервері сталася помилка, через яку він не зміг завершити виконання вашого " +"запиту: %1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"Під час виконання запиту виникла проблема. Збірку на сервері не було " +"вилучено.\n" +"%1 (%2)." + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "Отримано некоректні відповіді сервера" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "Помилка під час спроби встановлення фокусу для XQuery" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "Реалізацією DAV було надіслано некоректні дані XQuery" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"Під час виконання запиту виникла проблема. Збірку на сервері не було " +"змінено.\n" +"%1 (%2)." + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "Немає властивостей для зміни або вилучення" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "Під час спроби зміни властивостей сталася помилка" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"Сервером було повернуто додаткову інформацію:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Під час виконання запиту виникла проблема. Запис на сервері не було " +"створено.\n" +"%1 (%2)." + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"Під час виконання запиту виникла проблема. Запис на сервері не було " +"вилучено.\n" +"%1 (%2)." + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"Під час виконання запиту виникла проблема. Запис на сервері не було " +"змінено.\n" +"%1 (%2)." + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "Під час виконання запиту виникла проблема." + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "" +"Під час виконання запиту виникла проблема. Підтримки потрібних вам типів " +"MIME не передбачено." diff --git a/po/zh_CN/libkdav.po b/po/zh_CN/libkdav.po new file mode 100644 index 0000000..8ba9a4b --- /dev/null +++ b/po/zh_CN/libkdav.po @@ -0,0 +1,159 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: kdeorg\n" +"Report-Msgid-Bugs-To: http://bugs.kde.org\n" +"POT-Creation-Date: 2018-04-21 03:40+0200\n" +"PO-Revision-Date: 2018-11-01 23:18\n" +"Last-Translator: guoyunhe \n" +"Language-Team: Chinese Simplified\n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: crowdin.com\n" +"X-Crowdin-Project: kdeorg\n" +"X-Crowdin-Language: zh-CN\n" +"X-Crowdin-File: /kf5-stable/messages/pim/libkdav.pot\n" + +#: common/daverror.cpp:83 +#, kde-format +msgid "Invalid username/password" +msgstr "无效的用户名或密码" + +#: common/daverror.cpp:85 +#, kde-format +msgid "Access forbidden" +msgstr "禁止访问" + +#: common/daverror.cpp:87 +#, kde-format +msgid "Resource not found" +msgstr "找不到资源" + +#: common/daverror.cpp:89 +#, kde-format +msgid "HTTP error" +msgstr "HTTP 错误" + +#: common/daverror.cpp:91 +#, kde-format +msgid "" +"There was a problem with the request.\n" +"%1 (%2)." +msgstr "请求出错。\\n%1 (%2)。" + +#: common/daverror.cpp:95 +#, kde-format +msgid "Protocol for the collection does not support MULTIGET" +msgstr "集合的协议不支持 MULTIGET" + +#: common/daverror.cpp:98 +#, kde-format +msgid "" +"The server encountered an error that prevented it from completing your " +"request: %1 (%2)" +msgstr "服务器出现错误,无法完成您的请求:%1 (%2)" + +#: common/daverror.cpp:101 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been deleted " +"from the server.\n" +"%1 (%2)." +msgstr "" +"请求出错。服务器上的项目未删除。\n" +"%1 (%2)。" + +#: common/daverror.cpp:105 +#, kde-format +msgid "Invalid responses from backend" +msgstr "无效的后端应答" + +#: common/daverror.cpp:108 +#, kde-format +msgid "Error setting focus for XQuery" +msgstr "设置 XQuery 焦点时发生错误" + +#: common/daverror.cpp:111 +#, kde-format +msgid "Invalid XQuery submitted by DAV implementation" +msgstr "DAV 实现提交了无效的 XQuery" + +#: common/daverror.cpp:114 +#, kde-format +msgid "" +"There was a problem with the request. The collection has not been modified " +"on the server.\n" +"%1 (%2)." +msgstr "" +"请求出错。服务器上的项目未修改。\n" +"%1 (%2)。" + +#: common/daverror.cpp:118 +#, kde-format +msgid "No properties to change or remove" +msgstr "没有要更改或删除的属性" + +#: common/daverror.cpp:121 +#, kde-format +msgid "There was an error when modifying the properties" +msgstr "修改属性时出错" + +#: common/daverror.cpp:123 +#, kde-format +msgid "" +"\n" +"The server returned more information:\n" +"%1" +msgstr "" +"\n" +"服务器返回更多信息:\n" +"%1" + +#: common/daverror.cpp:127 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been created on the " +"server.\n" +"%1 (%2)." +msgstr "" +"请求出错。服务器上的项目未创建。\n" +"%1 (%2)。" + +#: common/daverror.cpp:131 +#, kde-format +msgid "" +"There was a problem with the request. The item has not been deleted from the " +"server.\n" +"%1 (%2)." +msgstr "" +"请求出错。服务器上的项目未删除。\n" +"%1 (%2)。" + +#: common/daverror.cpp:135 +#, kde-format +msgid "" +"There was a problem with the request. The item was not modified on the " +"server.\n" +"%1 (%2)." +msgstr "" +"请求出错。服务器上的项目未修改。\n" +"%1 (%2)。" + +#: common/daverror.cpp:140 +#, kde-format +msgid "There was a problem with the request." +msgstr "请求出错。" + +#: common/daverror.cpp:144 +#, kde-format +msgid "" +"There was a problem with the request. The requested MIME types are not " +"supported." +msgstr "请求出错。服务器上的项目未创建。" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..8ad0760 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,132 @@ +add_definitions(-DTRANSLATION_DOMAIN=\"libkdav\") +include_directories( + common/ + protocols/ +) + +########### next target ############### + +set(libkdav_SRCS + common/davjobbase.cpp + common/davcollection.cpp + common/davcollectiondeletejob.cpp + common/davcollectionsfetchjob.cpp + common/davcollectionmodifyjob.cpp + common/davcollectionsmultifetchjob.cpp + common/davprotocolbase.cpp + common/daverror.cpp + common/davitem.cpp + common/davitemcreatejob.cpp + common/davitemdeletejob.cpp + common/davitemfetchjob.cpp + common/davitemmodifyjob.cpp + common/davitemsfetchjob.cpp + common/davitemslistjob.cpp + common/davmanager.cpp + common/davmultigetprotocol.cpp + common/davprincipalhomesetsfetchjob.cpp + common/davprincipalsearchjob.cpp + common/davurl.cpp + common/utils.cpp + common/etagcache.cpp + + protocols/groupdavprotocol.cpp + + #KContacts + protocols/carddavprotocol.cpp + + #KCalCore + protocols/caldavprotocol.cpp +) + + +ecm_generate_headers(KDAV_Camelcase_HEADERS + HEADER_NAMES + DavJobBase + DavCollection + DavCollectionDeleteJob + DavCollectionsFetchJob + DavCollectionModifyJob + DavCollectionsMultiFetchJob + DavError + DavItem + DavItemCreateJob + DavItemDeleteJob + DavItemFetchJob + DavItemModifyJob + DavItemsFetchJob + DavItemsListJob + DavManager + DavProtocolBase + DavPrincipalHomesetsFetchJob + DavPrincipalSearchJob + DavUrl + Utils + Enums + EtagCache + REQUIRED_HEADERS KDAV_HEADERS + PREFIX KDAV + RELATIVE common + ) + +install(FILES + ${KDAV_Camelcase_HEADERS} + DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim/KDAV + COMPONENT Devel + ) + +install(FILES + ${KDAV_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/kpimkdav_export.h + ${CMAKE_CURRENT_BINARY_DIR}/libkdav_debug.h + + DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim/kdav + COMPONENT Devel + ) + +ecm_generate_pri_file(BASE_NAME kdav + LIB_NAME KPimKDAV + FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR}/KDAV + ) + +install(FILES + ${PRI_FILENAME} + DESTINATION ${ECM_MKSPECS_INSTALL_DIR} + ) + +ecm_qt_declare_logging_category(libkdav_SRCS HEADER libkdav_debug.h IDENTIFIER KDAV_LOG CATEGORY_NAME org.kde.pim.kdav) + +add_library(KPimKDAV + ${libkdav_SRCS} + ) + +generate_export_header(KPimKDAV BASE_NAME kpimkdav) + +add_library(KPim::KDAV ALIAS KPimKDAV) + +target_link_libraries(KPimKDAV +PUBLIC + KF5::CoreAddons +PRIVATE + Qt5::Xml + Qt5::Gui + Qt5::XmlPatterns + KF5::KIOCore + KF5::I18n + ) + +set_target_properties(KPimKDAV PROPERTIES + VERSION ${KDAV_VERSION_STRING} + SOVERSION ${KDAV_SOVERSION} + EXPORT_NAME KDAV + ) + +install(TARGETS + KPimKDAV + EXPORT KPimKDAVTargets + ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} + ${LIBRARY_NAMELINK} + ) + +target_include_directories(KPimKDAV INTERFACE "$") + diff --git a/src/Messages.sh b/src/Messages.sh new file mode 100644 index 0000000..ee93862 --- /dev/null +++ b/src/Messages.sh @@ -0,0 +1,2 @@ +#! /bin/sh +$XGETTEXT `find . -name "*.cpp" -o -name "*.h" | grep -v '/tests/'` -o $podir/libkdav.pot diff --git a/src/common/davcollection.cpp b/src/common/davcollection.cpp new file mode 100644 index 0000000..e0476ff --- /dev/null +++ b/src/common/davcollection.cpp @@ -0,0 +1,143 @@ +/* + Copyright (c) 2009 Grégory Oestreicher + + 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 "davcollection.h" + +#include "davurl.h" + +#include + +using namespace KDAV; + +class DavCollectionPrivate +{ +public: + DavCollectionPrivate(DavCollection *qPtr) : q(qPtr) + { + } + + void fillFrom(const DavCollectionPrivate &other); + + DavCollection *q; + QString mCTag; + DavUrl mUrl; + QString mDisplayName; + QColor mColor; + DavCollection::ContentTypes mContentTypes; + Privileges mPrivileges; +}; + +void DavCollectionPrivate::fillFrom(const DavCollectionPrivate &other) +{ + mCTag = other.mCTag; + mUrl = other.mUrl; + mDisplayName = other.mDisplayName; + mColor = other.mColor; + mContentTypes = other.mContentTypes; + mPrivileges = other.mPrivileges; +} + +DavCollection::DavCollection() + : d(std::unique_ptr(new DavCollectionPrivate(this))) +{ +} + +DavCollection::DavCollection(const DavUrl &url, const QString &displayName, ContentTypes contentTypes) + : d(std::unique_ptr(new DavCollectionPrivate(this))) +{ + d->mUrl = url; + d->mDisplayName = displayName; + d->mContentTypes = contentTypes; + d->mPrivileges = KDAV::All; +} + +DavCollection::DavCollection(const DavCollection &other) + : d(std::unique_ptr(new DavCollectionPrivate(this))) +{ + d->fillFrom(*other.d.get()); +} + +DavCollection &DavCollection::operator=(const DavCollection &other) +{ + d->fillFrom(*other.d.get()); + return *this; +} + +DavCollection::~DavCollection() +{ +} + +void DavCollection::setCTag(const QString &ctag) +{ + d->mCTag = ctag; +} + +QString DavCollection::CTag() const +{ + return d->mCTag; +} + +void DavCollection::setUrl(const DavUrl &url) +{ + d->mUrl = url; +} + +DavUrl DavCollection::url() const +{ + return d->mUrl; +} + +void DavCollection::setDisplayName(const QString &displayName) +{ + d->mDisplayName = displayName; +} + +QString DavCollection::displayName() const +{ + return d->mDisplayName; +} + +void DavCollection::setColor(const QColor &color) +{ + d->mColor = color; +} + +QColor DavCollection::color() const +{ + return d->mColor; +} + +void DavCollection::setContentTypes(ContentTypes contentTypes) +{ + d->mContentTypes = contentTypes; +} + +DavCollection::ContentTypes DavCollection::contentTypes() const +{ + return d->mContentTypes; +} + +void DavCollection::setPrivileges(Privileges privs) +{ + d->mPrivileges = privs; +} + +Privileges DavCollection::privileges() const +{ + return d->mPrivileges; +} diff --git a/src/common/davcollection.h b/src/common/davcollection.h new file mode 100644 index 0000000..5e16ec0 --- /dev/null +++ b/src/common/davcollection.h @@ -0,0 +1,156 @@ +/* + Copyright (c) 2009 Grégory Oestreicher + + 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 KDAV_DAVCOLLECTION_H +#define KDAV_DAVCOLLECTION_H + +#include "kpimkdav_export.h" + +#include "enums.h" + +#include + +#include +#include + +class QColor; + +class DavCollectionPrivate; + +namespace KDAV { +class DavUrl; +} + +namespace KDAV { +/** + * @short A helper class to store information about DAV collection. + * + * This class is used as container to transfer information about DAV + * collections between the Akonadi resource and the DAV jobs. + */ +class KPIMKDAV_EXPORT DavCollection +{ +public: + /** + * Defines a list of DAV collection objects. + */ + typedef QVector List; + + /** + * Describes the possible content type of the DAV collection. + */ + enum ContentType { + Events = 1, ///< The collection can contain event DAV resources. + Todos = 2, ///< The collection can contain todo DAV resources. + Contacts = 4, ///< The collection can contain contact DAV resources. + FreeBusy = 8, ///< The collection can contain free/busy information. + Journal = 16, ///< The collection can contain journal DAV resources. + Calendar = 32 ///< The collection can contain anything calendar-related. + }; + Q_DECLARE_FLAGS(ContentTypes, ContentType) + + /** + * Creates an empty DAV collection. + */ + DavCollection(); + + /** + * Creates a new DAV collection. + * + * @param url The url that identifies the collection. + * @param displayName The display name of the collection. + * @param contentTypes The possible content types of the collection. + */ + DavCollection(const DavUrl &url, const QString &displayName, ContentTypes contentTypes); + + DavCollection(const DavCollection &other); + DavCollection &operator=(const DavCollection &other); + + ~DavCollection(); + + /** + * Sets this collection CTag. + */ + void setCTag(const QString &ctag); + + /** + * Returns this collection CTag. The returned value will be empty + * if no CTag was found. + */ + Q_REQUIRED_RESULT QString CTag() const; + + /** + * Sets the @p url that identifies the collection. + */ + void setUrl(const DavUrl &url); + + /** + * Returns the url that identifies the collection. + */ + Q_REQUIRED_RESULT DavUrl url() const; + + /** + * Sets the display @p name of the collection. + */ + void setDisplayName(const QString &name); + + /** + * Returns the display name of the collection. + */ + Q_REQUIRED_RESULT QString displayName() const; + + /** + * Sets the color for this collection + */ + void setColor(const QColor &color); + + /** + * Return the color of the collection, or an empty string if + * none was provided by the backend. + */ + Q_REQUIRED_RESULT QColor color() const; + + /** + * Sets the possible content @p types of the collection. + */ + void setContentTypes(ContentTypes types); + + /** + * Returns the possible content types of the collection. + */ + Q_REQUIRED_RESULT ContentTypes contentTypes() const; + + /** + * Sets the privileges on this collection. + */ + void setPrivileges(Privileges privs); + + /** + * Returns the privileges on this collection. + */ + Q_REQUIRED_RESULT Privileges privileges() const; + +private: + std::unique_ptr d; +}; +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(KDAV::DavCollection::ContentTypes) +Q_DECLARE_TYPEINFO(KDAV::DavCollection, Q_MOVABLE_TYPE); + +#endif diff --git a/src/common/davcollectiondeletejob.cpp b/src/common/davcollectiondeletejob.cpp new file mode 100644 index 0000000..f3f3ef0 --- /dev/null +++ b/src/common/davcollectiondeletejob.cpp @@ -0,0 +1,61 @@ +/* + Copyright (c) 2010 Grégory Oestreicher + + 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 "davcollectiondeletejob.h" + +#include "daverror.h" + +#include +#include + +using namespace KDAV; + +DavCollectionDeleteJob::DavCollectionDeleteJob(const DavUrl &url, QObject *parent) + : DavJobBase(parent) + , mUrl(url) +{ +} + +void DavCollectionDeleteJob::start() +{ + KIO::DeleteJob *job = KIO::del(mUrl.url(), KIO::HideProgressInfo | KIO::DefaultFlags); + job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); + job->addMetaData(QStringLiteral("cookies"), QStringLiteral("none")); + job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); + + connect(job, &KIO::DeleteJob::result, this, &DavCollectionDeleteJob::davJobFinished); +} + +void DavCollectionDeleteJob::davJobFinished(KJob *job) +{ + KIO::DeleteJob *deleteJob = qobject_cast(job); + + if (deleteJob->error() && deleteJob->error() != KIO::ERR_NO_CONTENT) { + const int responseCode = deleteJob->queryMetaData(QStringLiteral("responsecode")).isEmpty() + ? 0 + : deleteJob->queryMetaData(QStringLiteral("responsecode")).toInt(); + + setLatestResponseCode(responseCode); + setError(ERR_COLLECTIONDELETE); + setJobErrorText(deleteJob->errorText()); + setJobError(deleteJob->error()); + setErrorTextFromDavError(); + } + + emitResult(); +} diff --git a/src/common/davcollectiondeletejob.h b/src/common/davcollectiondeletejob.h new file mode 100644 index 0000000..828fdc8 --- /dev/null +++ b/src/common/davcollectiondeletejob.h @@ -0,0 +1,61 @@ +/* + Copyright (c) 2010 Grégory Oestreicher + + 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 KDAV_DAVCOLLECTIONDELETEJOB_H +#define KDAV_DAVCOLLECTIONDELETEJOB_H + +#include "kpimkdav_export.h" + +#include "davjobbase.h" +#include "davurl.h" + +#include + +namespace KDAV { +/** + * @short A job that deletes a DAV collection. + * + * This job is used to delete a DAV collection at a certain URL. + */ +class KPIMKDAV_EXPORT DavCollectionDeleteJob : public DavJobBase +{ + Q_OBJECT + +public: + /** + * Creates a new DAV collection delete job. + * + * @param url The dav url of the collection to delete + * @param parent The parent object. + */ + explicit DavCollectionDeleteJob(const DavUrl &url, QObject *parent = nullptr); + + /** + * Starts the job. + */ + void start() override; + +private Q_SLOTS: + void davJobFinished(KJob *); + +private: + DavUrl mUrl; +}; +} + +#endif diff --git a/src/common/davcollectionmodifyjob.cpp b/src/common/davcollectionmodifyjob.cpp new file mode 100644 index 0000000..c8c04e9 --- /dev/null +++ b/src/common/davcollectionmodifyjob.cpp @@ -0,0 +1,159 @@ +/* + Copyright (c) 2010 Grégory Oestreicher + + 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 "davcollectionmodifyjob.h" +#include "davmanager.h" + +#include "daverror.h" +#include "utils.h" + +#include +#include + +using namespace KDAV; + +DavCollectionModifyJob::DavCollectionModifyJob(const DavUrl &url, QObject *parent) + : DavJobBase(parent) + , mUrl(url) +{ +} + +void DavCollectionModifyJob::setProperty(const QString &prop, const QString &value, const QString &ns) +{ + QDomElement propElement; + + if (ns.isEmpty()) { + propElement = mQuery.createElement(prop); + } else { + propElement = mQuery.createElementNS(ns, prop); + } + + const QDomText textElement = mQuery.createTextNode(value); + propElement.appendChild(textElement); + + mSetProperties << propElement; +} + +void DavCollectionModifyJob::removeProperty(const QString &prop, const QString &ns) +{ + QDomElement propElement; + + if (ns.isEmpty()) { + propElement = mQuery.createElement(prop); + } else { + propElement = mQuery.createElementNS(ns, prop); + } + + mRemoveProperties << propElement; +} + +void DavCollectionModifyJob::start() +{ + if (mSetProperties.isEmpty() && mRemoveProperties.isEmpty()) { + setError(ERR_COLLECTIONMODIFY_NO_PROPERITES); + setErrorTextFromDavError(); + emitResult(); + return; + } + + QDomDocument mQuery; + QDomElement propertyUpdateElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propertyupdate")); + mQuery.appendChild(propertyUpdateElement); + + if (!mSetProperties.isEmpty()) { + QDomElement setElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("set")); + propertyUpdateElement.appendChild(setElement); + + QDomElement propElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + setElement.appendChild(propElement); + + foreach (const QDomElement &element, mSetProperties) { + propElement.appendChild(element); + } + } + + if (!mRemoveProperties.isEmpty()) { + QDomElement removeElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("remove")); + propertyUpdateElement.appendChild(removeElement); + + QDomElement propElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + removeElement.appendChild(propElement); + + foreach (const QDomElement &element, mSetProperties) { + propElement.appendChild(element); + } + } + + KIO::DavJob *job = DavManager::self()->createPropPatchJob(mUrl.url(), mQuery); + job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); + connect(job, &KIO::DavJob::result, this, &DavCollectionModifyJob::davJobFinished); +} + +void DavCollectionModifyJob::davJobFinished(KJob *job) +{ + KIO::DavJob *davJob = qobject_cast(job); + const QString responseCodeStr = davJob->queryMetaData(QStringLiteral("responsecode")); + const int responseCode = responseCodeStr.isEmpty() + ? 0 + : responseCodeStr.toInt(); + + // KIO::DavJob does not set error() even if the HTTP status code is a 4xx or a 5xx + if (davJob->error() || (responseCode >= 400 && responseCode < 600)) { + setLatestResponseCode(responseCode); + setError(ERR_COLLECTIONMODIFY); + setJobErrorText(davJob->errorText()); + setJobError(davJob->error()); + setErrorTextFromDavError(); + emitResult(); + return; + } + + const QDomDocument response = davJob->response(); + QDomElement responseElement = Utils::firstChildElementNS(response.documentElement(), QStringLiteral("DAV:"), QStringLiteral("response")); + + bool hasError = false; + + // parse all propstats answers to get the eventual errors + const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat")); + for (int i = 0; i < propstats.length(); ++i) { + const QDomElement propstatElement = propstats.item(i).toElement(); + const QDomElement statusElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("status")); + + const QString statusText = statusElement.text(); + if (statusText.contains(QLatin1String("200"))) { + continue; + } else { + // Generic error + hasError = true; + break; + } + } + + if (hasError) { + setError(ERR_COLLECTIONMODIFY_RESPONSE); + + // Trying to get more information about the error + const QDomElement responseDescriptionElement = Utils::firstChildElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("responsedescription")); + if (!responseDescriptionElement.isNull()) { + setJobErrorText(responseDescriptionElement.text()); + } + setErrorTextFromDavError(); + } + + emitResult(); +} diff --git a/src/common/davcollectionmodifyjob.h b/src/common/davcollectionmodifyjob.h new file mode 100644 index 0000000..5517de0 --- /dev/null +++ b/src/common/davcollectionmodifyjob.h @@ -0,0 +1,83 @@ +/* + Copyright (c) 2010 Grégory Oestreicher + + 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 KDAV_DAVCOLLECTIONMODIFYJOB_H +#define KDAV_DAVCOLLECTIONMODIFYJOB_H + +#include "kpimkdav_export.h" + +#include "davjobbase.h" +#include "davurl.h" + +#include + +#include + +namespace KDAV { +/** + * @short A job that modifies a DAV collection. + * + * This job is used to modify a property of a DAV collection + * on the DAV server. + */ +class KPIMKDAV_EXPORT DavCollectionModifyJob : public DavJobBase +{ + Q_OBJECT + +public: + /** + * Creates a new dav collection modify job. + * + * @param url The DAV url that identifies the collection. + * @param parent The parent object. + */ + explicit DavCollectionModifyJob(const DavUrl &url, QObject *parent = nullptr); + + /** + * Sets the property that shall be modified by the job. + * + * @param property The name of the property. + * @param value The value of the property. + * @param ns The XML namespace that shall be used for the property name. + */ + void setProperty(const QString &property, const QString &value, const QString &ns = QString()); + + /** + * Sets the property that shall be removed by the job. + * + * @param property The name of the property. + * @param ns The XML namespace that shall be used for the property name. + */ + void removeProperty(const QString &property, const QString &ns); + + /** + * Starts the job. + */ + void start() override; + +private: + void davJobFinished(KJob *job); + DavUrl mUrl; + QDomDocument mQuery; + + QVector mSetProperties; + QVector mRemoveProperties; +}; +} + +#endif diff --git a/src/common/davcollectionsfetchjob.cpp b/src/common/davcollectionsfetchjob.cpp new file mode 100644 index 0000000..2f61cf3 --- /dev/null +++ b/src/common/davcollectionsfetchjob.cpp @@ -0,0 +1,356 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 "davcollectionsfetchjob.h" + +#include "davmanager.h" +#include "davprincipalhomesetsfetchjob.h" +#include "davprotocolbase.h" +#include "utils.h" +#include "daverror.h" + +#include "libkdav_debug.h" +#include +#include + +#include +#include +#include + +using namespace KDAV; + +DavCollectionsFetchJob::DavCollectionsFetchJob(const DavUrl &url, QObject *parent) + : DavJobBase(parent) + , mUrl(url) + , mSubJobCount(0) +{ +} + +void DavCollectionsFetchJob::start() +{ + if (DavManager::self()->davProtocol(mUrl.protocol())->supportsPrincipals()) { + DavPrincipalHomeSetsFetchJob *job = new DavPrincipalHomeSetsFetchJob(mUrl); + connect(job, &DavPrincipalHomeSetsFetchJob::result, this, &DavCollectionsFetchJob::principalFetchFinished); + job->start(); + } else { + doCollectionsFetch(mUrl.url()); + } +} + +DavCollection::List DavCollectionsFetchJob::collections() const +{ + return mCollections; +} + +DavUrl DavCollectionsFetchJob::davUrl() const +{ + return mUrl; +} + +void DavCollectionsFetchJob::doCollectionsFetch(const QUrl &url) +{ + ++mSubJobCount; + + const QDomDocument collectionQuery = DavManager::self()->davProtocol(mUrl.protocol())->collectionsQuery()->buildQuery(); + + KIO::DavJob *job = DavManager::self()->createPropFindJob(url, collectionQuery); + connect(job, &KIO::DavJob::result, this, &DavCollectionsFetchJob::collectionsFetchFinished); + job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); +} + +void DavCollectionsFetchJob::principalFetchFinished(KJob *job) +{ + const DavPrincipalHomeSetsFetchJob *davJob = qobject_cast(job); + + if (davJob->error()) { + if (davJob->latestResponseCode()) { + // If we have a HTTP response code then this may mean that + // the URL was not a principal URL. Retry as if it were a calendar URL. + qCDebug(KDAV_LOG) << job->errorText(); + doCollectionsFetch(mUrl.url()); + } else { + // Just give up here. + setDavError(davJob->davError()); + setErrorTextFromDavError(); + emitResult(); + } + + return; + } + + const QStringList homeSets = davJob->homeSets(); + qCDebug(KDAV_LOG) << "Found " << homeSets.size() << " homesets"; + qCDebug(KDAV_LOG) << homeSets; + + if (homeSets.isEmpty()) { + // Same as above, retry as if it were a calendar URL. + doCollectionsFetch(mUrl.url()); + return; + } + + for (const QString &homeSet : homeSets) { + QUrl url = mUrl.url(); + + if (homeSet.startsWith(QLatin1Char('/'))) { + // homeSet is only a path, use request url to complete + url.setPath(homeSet, QUrl::TolerantMode); + } else { + // homeSet is a complete url + QUrl tmpUrl(homeSet); + tmpUrl.setUserName(url.userName()); + tmpUrl.setPassword(url.password()); + url = tmpUrl; + } + + doCollectionsFetch(url); + } +} + +void DavCollectionsFetchJob::collectionsFetchFinished(KJob *job) +{ + KIO::DavJob *davJob = qobject_cast(job); + const QString responseCodeStr = davJob->queryMetaData(QStringLiteral("responsecode")); + const int responseCode = responseCodeStr.isEmpty() + ? 0 + : responseCodeStr.toInt(); + + // KIO::DavJob does not set error() even if the HTTP status code is a 4xx or a 5xx + if (davJob->error() || (responseCode >= 400 && responseCode < 600)) { + if (davJob->url() != mUrl.url()) { + // Retry as if the initial URL was a calendar URL. + // We can end up here when retrieving a homeset on + // which a PROPFIND resulted in an error + doCollectionsFetch(mUrl.url()); + --mSubJobCount; + return; + } + + setLatestResponseCode(responseCode); + setError(ERR_PROBLEM_WITH_REQUEST); + setJobErrorText(davJob->errorText()); + setJobError(davJob->error()); + setErrorTextFromDavError(); + } else { + // For use in the collectionDiscovered() signal + QUrl _jobUrl = mUrl.url(); + _jobUrl.setUserInfo(QString()); + const QString jobUrl = _jobUrl.toDisplayString(); + + // Validate that we got a valid PROPFIND response + QDomElement rootElement = davJob->response().documentElement(); + if (rootElement.tagName().compare(QLatin1String("multistatus"), Qt::CaseInsensitive) != 0) { + setError(ERR_COLLECTIONFETCH); + setErrorTextFromDavError(); + subjobFinished(); + return; + } + + QByteArray resp(davJob->response().toByteArray()); + QBuffer buffer(&resp); + buffer.open(QIODevice::ReadOnly); + + QXmlQuery xquery; + if (!xquery.setFocus(&buffer)) { + setError(ERR_COLLECTIONFETCH_XQUERY_SETFOCUS); + setErrorTextFromDavError(); + subjobFinished(); + return; + } + + xquery.setQuery(DavManager::self()->davProtocol(mUrl.protocol())->collectionsXQuery()); + if (!xquery.isValid()) { + setError(ERR_COLLECTIONFETCH_XQUERY_INVALID); + setErrorTextFromDavError(); + subjobFinished(); + return; + } + + QString responsesStr; + xquery.evaluateTo(&responsesStr); + responsesStr.prepend(QLatin1String("")); + responsesStr.append(QLatin1String("")); + + QDomDocument document; + if (!document.setContent(responsesStr, true)) { + setError(ERR_COLLECTIONFETCH); + setErrorTextFromDavError(); + subjobFinished(); + return; + } + + if (!error()) { + /* + * Extract information from a document like the following: + * + * + * + * /caldav.php/test1.user/home/ + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * Test1 User + * + * + * + * + * + * 12345 + * + * HTTP/1.1 200 OK + * + * + * + */ + + const QDomElement responsesElement = document.documentElement(); + + QDomElement responseElement = Utils::firstChildElementNS(responsesElement, QStringLiteral("DAV:"), QStringLiteral("response")); + while (!responseElement.isNull()) { + QDomElement propstatElement; + + // check for the valid propstat, without giving up on first error + { + const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat")); + for (int i = 0; i < propstats.length(); ++i) { + const QDomElement propstatCandidate = propstats.item(i).toElement(); + const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status")); + if (statusElement.text().contains(QLatin1String("200"))) { + propstatElement = propstatCandidate; + } + } + } + + if (propstatElement.isNull()) { + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + continue; + } + + // extract url + const QDomElement hrefElement = Utils::firstChildElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("href")); + if (hrefElement.isNull()) { + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + continue; + } + + QString href = hrefElement.text(); + if (!href.endsWith(QLatin1Char('/'))) { + href.append(QLatin1Char('/')); + } + + QUrl url = davJob->url(); + url.setUserInfo(QString()); + if (href.startsWith(QLatin1Char('/'))) { + // href is only a path, use request url to complete + url.setPath(href, QUrl::TolerantMode); + } else { + // href is a complete url + url = QUrl::fromUserInput(href); + } + + // don't add this resource if it has already been detected + bool alreadySeen = false; + for (const DavCollection &seen : qAsConst(mCollections)) { + if (seen.url().toDisplayString() == url.toDisplayString()) { + alreadySeen = true; + } + } + if (alreadySeen) { + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + continue; + } + + // extract display name + const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); + const QDomElement displaynameElement = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("displayname")); + const QString displayName = displaynameElement.text(); + + // Extract CTag + const QDomElement CTagElement = Utils::firstChildElementNS(propElement, QStringLiteral("http://calendarserver.org/ns/"), QStringLiteral("getctag")); + QString CTag; + if (!CTagElement.isNull()) { + CTag = CTagElement.text(); + } + + // extract calendar color if provided + const QDomElement colorElement = Utils::firstChildElementNS(propElement, QStringLiteral("http://apple.com/ns/ical/"), QStringLiteral("calendar-color")); + QColor color; + if (!colorElement.isNull()) { + QString colorValue = colorElement.text(); + if (QColor::isValidColor(colorValue)) { + // Color is either #RRGGBBAA or #RRGGBB but QColor expects #AARRGGBB + // so we put the AA in front if the string's length is 9. + if (colorValue.size() == 9) { + QString fixedColorValue = QStringLiteral("#") + colorValue.mid(7, 2) + colorValue.mid(1, 6); + color.setNamedColor(fixedColorValue); + } else { + color.setNamedColor(colorValue); + } + } + } + + // extract allowed content types + const DavCollection::ContentTypes contentTypes = DavManager::self()->davProtocol(mUrl.protocol())->collectionContentTypes(propstatElement); + + auto _url = url; + _url.setUserInfo(mUrl.url().userInfo()); + DavCollection collection(DavUrl(_url, mUrl.protocol()), displayName, contentTypes); + + collection.setCTag(CTag); + if (color.isValid()) { + collection.setColor(color); + } + + // extract privileges + const QDomElement currentPrivsElement = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("current-user-privilege-set")); + if (currentPrivsElement.isNull()) { + // Assume that we have all privileges + collection.setPrivileges(KDAV::All); + } else { + Privileges privileges = Utils::extractPrivileges(currentPrivsElement); + collection.setPrivileges(privileges); + } + + qCDebug(KDAV_LOG) << url.toDisplayString() << "PRIVS: " << collection.privileges(); + mCollections << collection; + Q_EMIT collectionDiscovered(mUrl.protocol(), url.toDisplayString(), jobUrl); + + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + } + } + } + + subjobFinished(); +} + +void DavCollectionsFetchJob::subjobFinished() +{ + if (--mSubJobCount == 0) { + emitResult(); + } +} diff --git a/src/common/davcollectionsfetchjob.h b/src/common/davcollectionsfetchjob.h new file mode 100644 index 0000000..ff5ff40 --- /dev/null +++ b/src/common/davcollectionsfetchjob.h @@ -0,0 +1,86 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 KDAV_DAVCOLLECTIONSFETCHJOB_H +#define KDAV_DAVCOLLECTIONSFETCHJOB_H + +#include "kpimkdav_export.h" + +#include "davcollection.h" +#include "davjobbase.h" +#include "davurl.h" + +#include + +namespace KDAV { +/** + * @short A job that fetches all DAV collection. + * + * This job is used to fetch all DAV collection that are available + * under a certain DAV url. + */ +class KPIMKDAV_EXPORT DavCollectionsFetchJob : public DavJobBase +{ + Q_OBJECT + +public: + /** + * Creates a new dav collections fetch job. + * + * @param url The DAV url of the DAV collection whose sub collections shall be fetched. + * @param parent The parent object. + */ + explicit DavCollectionsFetchJob(const DavUrl &url, QObject *parent = nullptr); + + /** + * Starts the job. + */ + void start() override; + + /** + * Returns the list of fetched DAV collections. + */ + Q_REQUIRED_RESULT DavCollection::List collections() const; + + /** + * Return the DavUrl used by this job + */ + Q_REQUIRED_RESULT DavUrl davUrl() const; + +Q_SIGNALS: + /** + * This signal is emitted every time a new collection has been discovered. + * + * @param collectionUrl The URL of the discovered collection + * @param configuredUrl The URL given to the job + */ + void collectionDiscovered(int protocol, const QString &collectionUrl, const QString &configuredUrl); + +private: + void principalFetchFinished(KJob *); + void collectionsFetchFinished(KJob *); + void doCollectionsFetch(const QUrl &url); + void subjobFinished(); + + DavUrl mUrl; + DavCollection::List mCollections; + uint mSubJobCount; +}; +} + +#endif diff --git a/src/common/davcollectionsmultifetchjob.cpp b/src/common/davcollectionsmultifetchjob.cpp new file mode 100644 index 0000000..7bbec55 --- /dev/null +++ b/src/common/davcollectionsmultifetchjob.cpp @@ -0,0 +1,65 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 "davcollectionsmultifetchjob.h" + +#include "davcollectionsfetchjob.h" + +using namespace KDAV; + +DavCollectionsMultiFetchJob::DavCollectionsMultiFetchJob(const DavUrl::List &urls, QObject *parent) + : KJob(parent) + , mUrls(urls) + , mSubJobCount(urls.size()) +{ +} + +void DavCollectionsMultiFetchJob::start() +{ + if (mUrls.isEmpty()) { + emitResult(); + } + + foreach (const DavUrl &url, mUrls) { + DavCollectionsFetchJob *job = new DavCollectionsFetchJob(url, this); + connect(job, &DavCollectionsFetchJob::result, this, &DavCollectionsMultiFetchJob::davJobFinished); + connect(job, &DavCollectionsFetchJob::collectionDiscovered, this, &DavCollectionsMultiFetchJob::collectionDiscovered); + job->start(); + } +} + +DavCollection::List DavCollectionsMultiFetchJob::collections() const +{ + return mCollections; +} + +void DavCollectionsMultiFetchJob::davJobFinished(KJob *job) +{ + DavCollectionsFetchJob *fetchJob = qobject_cast(job); + + if (job->error()) { + setError(job->error()); + setErrorText(job->errorText()); + } else { + mCollections << fetchJob->collections(); + } + + if (--mSubJobCount == 0) { + emitResult(); + } +} diff --git a/src/common/davcollectionsmultifetchjob.h b/src/common/davcollectionsmultifetchjob.h new file mode 100644 index 0000000..62ea025 --- /dev/null +++ b/src/common/davcollectionsmultifetchjob.h @@ -0,0 +1,79 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 KDAV_DAVCOLLECTIONSMULTIFETCHJOB_H +#define KDAV_DAVCOLLECTIONSMULTIFETCHJOB_H + +#include "kpimkdav_export.h" + +#include "davcollection.h" +#include "davurl.h" + +#include + +namespace KDAV { +/** + * @short A job that fetches all DAV collection. + * + * This job is used to fetch all DAV collection that are available + * under a certain list of DAV urls. + * + * @note This class just combines multiple calls of DavCollectionsFetchJob + * into one job. + */ +class KPIMKDAV_EXPORT DavCollectionsMultiFetchJob : public KJob +{ + Q_OBJECT + +public: + /** + * Creates a new dav collections multi fetch job. + * + * @param urls The list of DAV urls whose sub collections shall be fetched. + * @param parent The parent object. + */ + explicit DavCollectionsMultiFetchJob(const DavUrl::List &urls, QObject *parent = nullptr); + + /** + * Starts the job. + */ + void start() override; + + /** + * Returns the list of fetched DAV collections. + */ + Q_REQUIRED_RESULT DavCollection::List collections() const; + +Q_SIGNALS: + /** + * This signal is emitted every time a new collection has been discovered. + * + * @param collectionUrl The URL of the discovered collection + * @param configuredUrl The URL given to the job + */ + void collectionDiscovered(int protocol, const QString &collectionUrl, const QString &configuredUrl); + +private: + void davJobFinished(KJob *); + DavUrl::List mUrls; + DavCollection::List mCollections; + uint mSubJobCount; +}; +} + +#endif diff --git a/src/common/daverror.cpp b/src/common/daverror.cpp new file mode 100644 index 0000000..f0bc514 --- /dev/null +++ b/src/common/daverror.cpp @@ -0,0 +1,150 @@ +/* + Copyright (c) 2016 Sandro Knauß + + 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 "daverror.h" +#include + +#include +#include + +using namespace KDAV; + +Error::Error() + : mErrorNumber(NO_ERR) + , mResponseCode(0) + , mJobErrorCode(0) +{ +} + +Error::Error(ErrorNumber errNo, int responseCode, const QString &errorText, int jobErrorCode) + : mErrorNumber(errNo) + , mResponseCode(responseCode) + , mErrorText(errorText) + , mJobErrorCode(jobErrorCode) +{ +} + +ErrorNumber Error::errorNumber() const +{ + return mErrorNumber; +} + +QString Error::internalErrorText() const +{ + return mErrorText; +} + +int Error::jobErrorCode() const +{ + return mJobErrorCode; +} + +int Error::responseCode() const +{ + return mResponseCode; +} + +QString KDAV::Error::translatedJobError() const +{ + QString err; + if (mJobErrorCode > 0 && mJobErrorCode != KIO::ERR_SLAVE_DEFINED) { + err = KIO::buildErrorString(mJobErrorCode, mErrorText); + } else { + err = mErrorText; + } + return err; +} + +QString Error::errorText() const +{ + QString result; + + QString err = translatedJobError(); + + switch (mErrorNumber) { + case ERR_PROBLEM_WITH_REQUEST: + // User-side error + if (mResponseCode == 401) { + err = i18n("Invalid username/password"); + } else if (mResponseCode == 403) { + err = i18n("Access forbidden"); + } else if (mResponseCode == 404) { + err = i18n("Resource not found"); + } else { + err = i18n("HTTP error"); + } + result = i18n("There was a problem with the request.\n" + "%1 (%2).", err, mResponseCode); + break; + case ERR_NO_MULTIGET: + result = i18n("Protocol for the collection does not support MULTIGET"); + break; + case ERR_SERVER_UNRECOVERABLE: + result = i18n("The server encountered an error that prevented it from completing your request: %1 (%2)", err, mResponseCode); + break; + case ERR_COLLECTIONDELETE: + result = i18n("There was a problem with the request. The collection has not been deleted from the server.\n" + "%1 (%2).", err, mResponseCode); + break; + case ERR_COLLECTIONFETCH: + result = i18n("Invalid responses from backend"); + break; + case ERR_COLLECTIONFETCH_XQUERY_SETFOCUS: + result = i18n("Error setting focus for XQuery"); + break; + case ERR_COLLECTIONFETCH_XQUERY_INVALID: + result = i18n("Invalid XQuery submitted by DAV implementation"); + break; + case ERR_COLLECTIONMODIFY: + result = i18n("There was a problem with the request. The collection has not been modified on the server.\n" + "%1 (%2).", err, mResponseCode); + break; + case ERR_COLLECTIONMODIFY_NO_PROPERITES: + result = i18n("No properties to change or remove"); + break; + case ERR_COLLECTIONMODIFY_RESPONSE: + result = i18n("There was an error when modifying the properties"); + if (!mErrorText.isEmpty()) { + result.append(i18n("\nThe server returned more information:\n%1", mErrorText)); + } + break; + case ERR_ITEMCREATE: + result = i18n("There was a problem with the request. The item has not been created on the server.\n" + "%1 (%2).", err, mResponseCode); + break; + case ERR_ITEMDELETE: + result = i18n("There was a problem with the request. The item has not been deleted from the server.\n" + "%1 (%2).", err, mResponseCode); + break; + case ERR_ITEMMODIFY: + result = i18n("There was a problem with the request. The item was not modified on the server.\n" + "%1 (%2).", err, mResponseCode); + break; + case ERR_ITEMLIST: + { + result = i18n("There was a problem with the request."); + break; + }; + case ERR_ITEMLIST_NOMIMETYPE: + result = i18n("There was a problem with the request. The requested MIME types are not supported."); + break; + case NO_ERR: + break; + } + return result; +} diff --git a/src/common/daverror.h b/src/common/daverror.h new file mode 100644 index 0000000..e286ffe --- /dev/null +++ b/src/common/daverror.h @@ -0,0 +1,69 @@ +/* + Copyright (c) 2016 Sandro Knauß + + 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 KDAV_DAVERROR_H +#define KDAV_DAVERROR_H + +#include "kpimkdav_export.h" + +#include + +#include + +namespace KDAV { +enum ErrorNumber { + NO_ERR = 0, + ERR_PROBLEM_WITH_REQUEST = KJob::UserDefinedError + 200, //it would be better to request KIO about uts UserDefinedError space. + ERR_NO_MULTIGET, + ERR_SERVER_UNRECOVERABLE, + ERR_COLLECTIONDELETE = ERR_PROBLEM_WITH_REQUEST + 10, + ERR_COLLECTIONFETCH = ERR_PROBLEM_WITH_REQUEST + 20, + ERR_COLLECTIONFETCH_XQUERY_SETFOCUS, + ERR_COLLECTIONFETCH_XQUERY_INVALID, + ERR_COLLECTIONMODIFY = ERR_PROBLEM_WITH_REQUEST + 30, + ERR_COLLECTIONMODIFY_NO_PROPERITES, + ERR_COLLECTIONMODIFY_RESPONSE, + ERR_ITEMCREATE = ERR_PROBLEM_WITH_REQUEST + 100, + ERR_ITEMDELETE = ERR_PROBLEM_WITH_REQUEST + 110, + ERR_ITEMMODIFY = ERR_PROBLEM_WITH_REQUEST + 120, + ERR_ITEMLIST = ERR_PROBLEM_WITH_REQUEST + 130, + ERR_ITEMLIST_NOMIMETYPE +}; + +class KPIMKDAV_EXPORT Error +{ +public: + explicit Error(); + explicit Error(ErrorNumber errNo, int responseCode, const QString &errorText, int jobErrorCode); + + Q_REQUIRED_RESULT ErrorNumber errorNumber() const; + Q_REQUIRED_RESULT int responseCode() const; + Q_REQUIRED_RESULT QString internalErrorText() const; + Q_REQUIRED_RESULT int jobErrorCode() const; + Q_REQUIRED_RESULT QString translatedJobError() const; + Q_REQUIRED_RESULT QString errorText() const; + +private: + ErrorNumber mErrorNumber; + int mResponseCode; + QString mErrorText; + int mJobErrorCode; +}; +} + +#endif diff --git a/src/common/davitem.cpp b/src/common/davitem.cpp new file mode 100644 index 0000000..82236d2 --- /dev/null +++ b/src/common/davitem.cpp @@ -0,0 +1,144 @@ +/* + Copyright (c) 2009 Grégory Oestreicher + + 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 "davitem.h" + +#include "davurl.h" + +using namespace KDAV; + +class DavItemPrivate +{ +public: + DavItemPrivate(DavItem *qPtr) : q(qPtr) + { + } + + void fillFrom(const DavItemPrivate &other); + + DavItem *q; + + DavUrl mUrl; + QString mContentType; + QByteArray mData; + QString mEtag; +}; + +void DavItemPrivate::fillFrom(const DavItemPrivate &other) +{ + mUrl = other.mUrl; + mContentType = other.mContentType; + mData = other.mData; + mEtag = other.mEtag; +} + +DavItem::DavItem() + : d(std::unique_ptr(new DavItemPrivate(this))) +{ +} + +DavItem::DavItem(const DavUrl &url, const QString &contentType, const QByteArray &data, const QString &etag) + : d(std::unique_ptr(new DavItemPrivate(this))) +{ + d->mUrl = url; + d->mContentType = contentType; + d->mData = data; + d->mEtag = etag; +} + +DavItem::DavItem(const DavItem &other) + : d(std::unique_ptr(new DavItemPrivate(this))) +{ + d->fillFrom(*other.d.get()); +} + +DavItem &DavItem::operator=(const DavItem &other) +{ + d->fillFrom(*other.d.get()); + return *this; +} + +DavItem::~DavItem() +{ +} + +void DavItem::setUrl(const DavUrl &url) +{ + d->mUrl = url; +} + +DavUrl DavItem::url() const +{ + return d->mUrl; +} + +void DavItem::setContentType(const QString &contentType) +{ + d->mContentType = contentType; +} + +QString DavItem::contentType() const +{ + return d->mContentType; +} + +void DavItem::setData(const QByteArray &data) +{ + d->mData = data; +} + +QByteArray DavItem::data() const +{ + return d->mData; +} + +void DavItem::setEtag(const QString &etag) +{ + d->mEtag = etag; +} + +QString DavItem::etag() const +{ + return d->mEtag; +} + +QDataStream &KDAV::operator<<(QDataStream &stream, const DavItem &item) +{ + stream << item.url(); + stream << item.contentType(); + stream << item.data(); + stream << item.etag(); + + return stream; +} + +QDataStream &KDAV::operator>>(QDataStream &stream, DavItem &item) +{ + QString contentType, etag; + DavUrl url; + QByteArray data; + + stream >> url; + stream >> contentType; + stream >> data; + stream >> etag; + + item = DavItem(url, contentType, data, etag); + + return stream; +} diff --git a/src/common/davitem.h b/src/common/davitem.h new file mode 100644 index 0000000..9b3c05f --- /dev/null +++ b/src/common/davitem.h @@ -0,0 +1,125 @@ +/* + Copyright (c) 2009 Grégory Oestreicher + + 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 KDAV_DAVITEM_H +#define KDAV_DAVITEM_H + +#include "kpimkdav_export.h" + +#include + +#include +#include +#include +#include + +class DavItemPrivate; + +namespace KDAV { +class DavUrl; +} + +namespace KDAV { +/** + * @short A helper class to store information about DAV resources. + * + * This class is used as container to transfer information about DAV + * resources between the Akonadi resource and the DAV jobs. + * + * @note While the DAV RFC names them DAV resource we call them items + * to comply to Akonadi terminology. + */ +class KPIMKDAV_EXPORT DavItem +{ +public: + /** + * Defines a list of DAV item objects. + */ + typedef QVector List; + + /** + * Creates an empty DAV item. + */ + DavItem(); + + /** + * Creates a new DAV item. + * + * @param url The url that identifies the item. + * @param contentType The content type of the item. + * @param data The actual raw content data of the item. + * @param etag The etag of the item. + */ + DavItem(const DavUrl &url, const QString &contentType, const QByteArray &data, const QString &etag); + + DavItem(const DavItem &other); + DavItem &operator=(const DavItem &other); + + ~DavItem(); + + /** + * Sets the @p url that identifies the item. + */ + void setUrl(const DavUrl &url); + + /** + * Returns the url that identifies the item. + */ + Q_REQUIRED_RESULT DavUrl url() const; + + /** + * Sets the content @p type of the item. + */ + void setContentType(const QString &type); + + /** + * Returns the content type of the item. + */ + Q_REQUIRED_RESULT QString contentType() const; + + /** + * Sets the raw content @p data of the item. + */ + void setData(const QByteArray &data); + + /** + * Returns the raw content data of the item. + */ + Q_REQUIRED_RESULT QByteArray data() const; + + /** + * Sets the @p etag of the item. + */ + void setEtag(const QString &etag); + + /** + * Returns the etag of the item. + */ + Q_REQUIRED_RESULT QString etag() const; + +private: + std::unique_ptr d; +}; + +KPIMKDAV_EXPORT QDataStream &operator<<(QDataStream &out, const DavItem &item); +KPIMKDAV_EXPORT QDataStream &operator>>(QDataStream &in, DavItem &item); +} + +Q_DECLARE_TYPEINFO(KDAV::DavItem, Q_MOVABLE_TYPE); + +#endif diff --git a/src/common/davitemcreatejob.cpp b/src/common/davitemcreatejob.cpp new file mode 100644 index 0000000..98495a6 --- /dev/null +++ b/src/common/davitemcreatejob.cpp @@ -0,0 +1,135 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 "davitemcreatejob.h" + +#include "davitemfetchjob.h" +#include "davmanager.h" +#include "daverror.h" + +#include +#include + +#include "libkdav_debug.h" + +using namespace KDAV; + +DavItemCreateJob::DavItemCreateJob(const DavItem &item, QObject *parent) + : DavJobBase(parent) + , mItem(item) + , mRedirectCount(0) +{ +} + +void DavItemCreateJob::start() +{ + QString headers = QStringLiteral("Content-Type: "); + headers += mItem.contentType(); + headers += QLatin1String("\r\n"); + headers += QLatin1String("If-None-Match: *"); + + KIO::StoredTransferJob *job = KIO::storedPut(mItem.data(), itemUrl(), -1, KIO::HideProgressInfo | KIO::DefaultFlags); + job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); + job->addMetaData(QStringLiteral("customHTTPHeader"), headers); + job->addMetaData(QStringLiteral("cookies"), QStringLiteral("none")); + job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); + job->setRedirectionHandlingEnabled(false); + + connect(job, &KIO::StoredTransferJob::result, this, &DavItemCreateJob::davJobFinished); +} + +DavItem DavItemCreateJob::item() const +{ + return mItem; +} + +QUrl DavItemCreateJob::itemUrl() const +{ + return mItem.url().url(); +} + +void DavItemCreateJob::davJobFinished(KJob *job) +{ + KIO::StoredTransferJob *storedJob = qobject_cast(job); + const QString responseCodeStr = storedJob->queryMetaData(QStringLiteral("responsecode")); + const int responseCode = responseCodeStr.isEmpty() + ? 0 + : responseCodeStr.toInt(); + if (storedJob->error()) { + setLatestResponseCode(responseCode); + setError(ERR_ITEMCREATE); + setJobErrorText(storedJob->errorText()); + setJobError(storedJob->error()); + setErrorTextFromDavError(); + + emitResult(); + return; + } + + // The 'Location:' HTTP header is used to indicate the new URL + const QStringList allHeaders = storedJob->queryMetaData(QStringLiteral("HTTP-Headers")).split(QLatin1Char('\n')); + QString location; + foreach (const QString &header, allHeaders) { + if (header.startsWith(QLatin1String("location:"), Qt::CaseInsensitive)) { + location = header.section(QLatin1Char(' '), 1); + } + } + + QUrl url; + if (location.isEmpty()) { + url = storedJob->url(); + } else if (location.startsWith(QLatin1Char('/'))) { + url = storedJob->url(); + url.setPath(location, QUrl::TolerantMode); + } else { + url = QUrl::fromUserInput(location); + } + + if (responseCode == 301 || responseCode == 302 || responseCode == 307 || responseCode == 308) { + if (mRedirectCount > 4) { + setLatestResponseCode(responseCode); + setError(UserDefinedError + responseCode); + emitResult(); + } else { + QUrl _itemUrl(url); + _itemUrl.setUserInfo(itemUrl().userInfo()); + mItem.setUrl(DavUrl(_itemUrl, mItem.url().protocol())); + + ++mRedirectCount; + start(); + } + + return; + } + + url.setUserInfo(itemUrl().userInfo()); + mItem.setUrl(DavUrl(url, mItem.url().protocol())); + + DavItemFetchJob *fetchJob = new DavItemFetchJob(mItem); + connect(fetchJob, &DavItemFetchJob::result, this, &DavItemCreateJob::itemRefreshed); + fetchJob->start(); +} + +void DavItemCreateJob::itemRefreshed(KJob *job) +{ + if (!job->error()) { + DavItemFetchJob *fetchJob = qobject_cast(job); + mItem.setEtag(fetchJob->item().etag()); + } + emitResult(); +} diff --git a/src/common/davitemcreatejob.h b/src/common/davitemcreatejob.h new file mode 100644 index 0000000..95afb97 --- /dev/null +++ b/src/common/davitemcreatejob.h @@ -0,0 +1,66 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 KDAV_DAVITEMCREATEJOB_H +#define KDAV_DAVITEMCREATEJOB_H + +#include "kpimkdav_export.h" + +#include "davitem.h" +#include "davjobbase.h" +#include "davurl.h" + +namespace KDAV { +/** + * @short A job to create a DAV item on the DAV server. + */ +class KPIMKDAV_EXPORT DavItemCreateJob : public DavJobBase +{ + Q_OBJECT + +public: + /** + * Creates a new dav item create job. + * + * @param item The item that shall be created. + * @param parent The parent object. + */ + DavItemCreateJob(const DavItem &item, QObject *parent = nullptr); + + /** + * Starts the job. + */ + void start() override; + + /** + * Returns the created DAV item including the correct identifier url + * and current etag information. + */ + Q_REQUIRED_RESULT DavItem item() const; + + Q_REQUIRED_RESULT QUrl itemUrl() const; + +private: + void davJobFinished(KJob *); + void itemRefreshed(KJob *); + DavItem mItem; + int mRedirectCount; +}; +} + +#endif diff --git a/src/common/davitemdeletejob.cpp b/src/common/davitemdeletejob.cpp new file mode 100644 index 0000000..795def8 --- /dev/null +++ b/src/common/davitemdeletejob.cpp @@ -0,0 +1,96 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 "davitemdeletejob.h" + +#include "davitemfetchjob.h" +#include "davmanager.h" +#include "daverror.h" + +#include +#include + +using namespace KDAV; + +DavItemDeleteJob::DavItemDeleteJob(const DavItem &item, QObject *parent) + : DavJobBase(parent) + , mItem(item) + , mFreshResponseCode(-1) +{ +} + +void DavItemDeleteJob::start() +{ + KIO::DeleteJob *job = KIO::del(mItem.url().url(), KIO::HideProgressInfo | KIO::DefaultFlags); + job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); + job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("If-Match: ") + mItem.etag()); + job->addMetaData(QStringLiteral("cookies"), QStringLiteral("none")); + job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); + + connect(job, &KIO::DeleteJob::result, this, &DavItemDeleteJob::davJobFinished); +} + +DavItem DavItemDeleteJob::freshItem() const +{ + return mFreshItem; +} + +int DavItemDeleteJob::freshResponseCode() const +{ + return mFreshResponseCode; +} + +void DavItemDeleteJob::davJobFinished(KJob *job) +{ + KIO::DeleteJob *deleteJob = qobject_cast(job); + + if (deleteJob->error() && deleteJob->error() != KIO::ERR_NO_CONTENT) { + const int responseCode = deleteJob->queryMetaData(QStringLiteral("responsecode")).isEmpty() + ? 0 + : deleteJob->queryMetaData(QStringLiteral("responsecode")).toInt(); + + if (responseCode != 404 && responseCode != 410) { + setLatestResponseCode(responseCode); + setError(ERR_ITEMDELETE); + setJobErrorText(deleteJob->errorText()); + setJobError(deleteJob->error()); + setErrorTextFromDavError(); + } + + if (hasConflict()) { + DavItemFetchJob *fetchJob = new DavItemFetchJob(mItem); + connect(fetchJob, &DavItemFetchJob::result, this, &DavItemDeleteJob::conflictingItemFetched); + fetchJob->start(); + return; + } + } + + emitResult(); +} + +void DavItemDeleteJob::conflictingItemFetched(KJob *job) +{ + DavItemFetchJob *fetchJob = qobject_cast(job); + mFreshResponseCode = fetchJob->latestResponseCode(); + + if (!job->error()) { + mFreshItem = fetchJob->item(); + } + + emitResult(); +} diff --git a/src/common/davitemdeletejob.h b/src/common/davitemdeletejob.h new file mode 100644 index 0000000..0e8f1bb --- /dev/null +++ b/src/common/davitemdeletejob.h @@ -0,0 +1,69 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 KDAV_DAVITEMDELETEJOB_H +#define KDAV_DAVITEMDELETEJOB_H + +#include "kpimkdav_export.h" + +#include "davitem.h" +#include "davjobbase.h" +#include "davurl.h" + +namespace KDAV { +/** + * @short A job to delete a DAV item on the DAV server. + */ +class KPIMKDAV_EXPORT DavItemDeleteJob : public DavJobBase +{ + Q_OBJECT + +public: + /** + * Creates a new dav item delete job. + * + * @param item The item that shall be deleted. + * @param parent The parent object. + */ + DavItemDeleteJob(const DavItem &item, QObject *parent = nullptr); + + /** + * Starts the job. + */ + void start() override; + + /** + * Returns the item that triggered the conflict, if any. + */ + Q_REQUIRED_RESULT DavItem freshItem() const; + + /** + * Returns the response code we got when fetching the fresh item. + */ + Q_REQUIRED_RESULT int freshResponseCode() const; + +private: + void davJobFinished(KJob *); + void conflictingItemFetched(KJob *); + DavItem mItem; + DavItem mFreshItem; + int mFreshResponseCode; +}; +} + +#endif diff --git a/src/common/davitemfetchjob.cpp b/src/common/davitemfetchjob.cpp new file mode 100644 index 0000000..4a35bf0 --- /dev/null +++ b/src/common/davitemfetchjob.cpp @@ -0,0 +1,91 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 "davitemfetchjob.h" + +#include "davmanager.h" +#include "daverror.h" + +#include +#include + +using namespace KDAV; + +static QString etagFromHeaders(const QString &headers) +{ + const QStringList allHeaders = headers.split(QLatin1Char('\n')); + + QString etag; + for (const QString &header : allHeaders) { + if (header.startsWith(QLatin1String("etag:"), Qt::CaseInsensitive)) { + etag = header.section(QLatin1Char(' '), 1); + } + } + + return etag; +} + +DavItemFetchJob::DavItemFetchJob(const DavItem &item, QObject *parent) + : DavJobBase(parent) + , mItem(item) +{ +} + +void DavItemFetchJob::start() +{ + KIO::StoredTransferJob *job = KIO::storedGet(mItem.url().url(), KIO::Reload, KIO::HideProgressInfo | KIO::DefaultFlags); + job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); + // Work around a strange bug in Zimbra (seen at least on CE 5.0.18) : if the user-agent + // contains "Mozilla", some strange debug data is displayed in the shared calendars. + // This kinda mess up the events parsing... + job->addMetaData(QStringLiteral("UserAgent"), QStringLiteral("KDE DAV groupware client")); + job->addMetaData(QStringLiteral("cookies"), QStringLiteral("none")); + job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); + + connect(job, &KIO::StoredTransferJob::result, this, &DavItemFetchJob::davJobFinished); +} + +DavItem DavItemFetchJob::item() const +{ + return mItem; +} + +void DavItemFetchJob::davJobFinished(KJob *job) +{ + KIO::StoredTransferJob *storedJob = qobject_cast(job); + const QString responseCodeStr = storedJob->queryMetaData(QStringLiteral("responsecode")); + const int responseCode = responseCodeStr.isEmpty() + ? 0 + : responseCodeStr.toInt(); + + setLatestResponseCode(responseCode); + + if (storedJob->error()) { + setLatestResponseCode(responseCode); + setError(ERR_PROBLEM_WITH_REQUEST); + setJobErrorText(storedJob->errorText()); + setJobError(storedJob->error()); + setErrorTextFromDavError(); + } else { + mItem.setData(storedJob->data()); + mItem.setContentType(storedJob->queryMetaData(QStringLiteral("content-type"))); + mItem.setEtag(etagFromHeaders(storedJob->queryMetaData(QStringLiteral("HTTP-Headers")))); + } + + emitResult(); +} diff --git a/src/common/davitemfetchjob.h b/src/common/davitemfetchjob.h new file mode 100644 index 0000000..5d7e980 --- /dev/null +++ b/src/common/davitemfetchjob.h @@ -0,0 +1,62 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 KDAV_DAVITEMFETCHJOB_H +#define KDAV_DAVITEMFETCHJOB_H + +#include "kpimkdav_export.h" + +#include "davitem.h" +#include "davjobbase.h" +#include "davurl.h" + +namespace KDAV { +/** + * @short A job that fetches a DAV item from the DAV server. + */ +class KPIMKDAV_EXPORT DavItemFetchJob : public DavJobBase +{ + Q_OBJECT + +public: + /** + * Creates a new dav item fetch job. + * + * @param item The item that shall be fetched. + * @param parent The parent object. + */ + DavItemFetchJob(const DavItem &item, QObject *parent = nullptr); + + /** + * Starts the job. + */ + void start() override; + + /** + * Returns the fetched item including current etag information. + */ + Q_REQUIRED_RESULT DavItem item() const; + +private: + void davJobFinished(KJob *); + DavUrl mUrl; + DavItem mItem; +}; +} + +#endif diff --git a/src/common/davitemmodifyjob.cpp b/src/common/davitemmodifyjob.cpp new file mode 100644 index 0000000..e9cf4ea --- /dev/null +++ b/src/common/davitemmodifyjob.cpp @@ -0,0 +1,146 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 "davitemmodifyjob.h" + +#include "davitemfetchjob.h" +#include "davmanager.h" +#include "daverror.h" + +#include + +using namespace KDAV; + +DavItemModifyJob::DavItemModifyJob(const DavItem &item, QObject *parent) + : DavJobBase(parent) + , mItem(item) + , mFreshResponseCode(0) +{ +} + +void DavItemModifyJob::start() +{ + QString headers = QStringLiteral("Content-Type: "); + headers += mItem.contentType(); + headers += QLatin1String("\r\n"); + headers += QLatin1String("If-Match: ") + mItem.etag(); + + KIO::StoredTransferJob *job = KIO::storedPut(mItem.data(), itemUrl(), -1, KIO::HideProgressInfo | KIO::DefaultFlags); + job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); + job->addMetaData(QStringLiteral("customHTTPHeader"), headers); + job->addMetaData(QStringLiteral("cookies"), QStringLiteral("none")); + job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); + + connect(job, &KIO::StoredTransferJob::result, this, &DavItemModifyJob::davJobFinished); +} + +DavItem DavItemModifyJob::item() const +{ + return mItem; +} + +DavItem DavItemModifyJob::freshItem() const +{ + return mFreshItem; +} + +int DavItemModifyJob::freshResponseCode() const +{ + return mFreshResponseCode; +} + +QUrl DavItemModifyJob::itemUrl() const +{ + return mItem.url().url(); +} + +void DavItemModifyJob::davJobFinished(KJob *job) +{ + KIO::StoredTransferJob *storedJob = qobject_cast(job); + + if (storedJob->error()) { + const int responseCode = storedJob->queryMetaData(QStringLiteral("responsecode")).isEmpty() + ? 0 + : storedJob->queryMetaData(QStringLiteral("responsecode")).toInt(); + + setLatestResponseCode(responseCode); + setError(ERR_ITEMMODIFY); + setJobErrorText(storedJob->errorText()); + setJobError(storedJob->error()); + setErrorTextFromDavError(); + + if (hasConflict()) { + DavItemFetchJob *fetchJob = new DavItemFetchJob(mItem); + connect(fetchJob, &DavItemFetchJob::result, this, &DavItemModifyJob::conflictingItemFetched); + fetchJob->start(); + } else { + emitResult(); + } + + return; + } + + // The 'Location:' HTTP header is used to indicate the new URL + const QStringList allHeaders = storedJob->queryMetaData(QStringLiteral("HTTP-Headers")).split(QLatin1Char('\n')); + QString location; + for (const QString &header : allHeaders) { + if (header.startsWith(QLatin1String("location:"), Qt::CaseInsensitive)) { + location = header.section(QLatin1Char(' '), 1); + } + } + + QUrl url; + if (location.isEmpty()) { + url = storedJob->url(); + } else if (location.startsWith(QLatin1Char('/'))) { + url = storedJob->url(); + url.setPath(location, QUrl::TolerantMode); + } else { + url = QUrl::fromUserInput(location); + } + + url.setUserInfo(itemUrl().userInfo()); + mItem.setUrl(DavUrl(url, mItem.url().protocol())); + + DavItemFetchJob *fetchJob = new DavItemFetchJob(mItem); + connect(fetchJob, &DavItemFetchJob::result, this, &DavItemModifyJob::itemRefreshed); + fetchJob->start(); +} + +void DavItemModifyJob::itemRefreshed(KJob *job) +{ + if (!job->error()) { + DavItemFetchJob *fetchJob = qobject_cast(job); + mItem.setEtag(fetchJob->item().etag()); + } else { + mItem.setEtag(QString()); + } + emitResult(); +} + +void DavItemModifyJob::conflictingItemFetched(KJob *job) +{ + DavItemFetchJob *fetchJob = qobject_cast(job); + mFreshResponseCode = fetchJob->latestResponseCode(); + + if (!job->error()) { + mFreshItem = fetchJob->item(); + } + + emitResult(); +} diff --git a/src/common/davitemmodifyjob.h b/src/common/davitemmodifyjob.h new file mode 100644 index 0000000..38e3c0b --- /dev/null +++ b/src/common/davitemmodifyjob.h @@ -0,0 +1,77 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 KDAV_DAVITEMMODIFYJOB_H +#define KDAV_DAVITEMMODIFYJOB_H + +#include "kpimkdav_export.h" + +#include "davitem.h" +#include "davjobbase.h" +#include "davurl.h" + +namespace KDAV { +/** + * @short A job that modifies a DAV item on the DAV server. + */ +class KPIMKDAV_EXPORT DavItemModifyJob : public DavJobBase +{ + Q_OBJECT + +public: + /** + * Creates a new dav item modify job. + * + * @param item The item that shall be modified. + * @param parent The parent object. + */ + DavItemModifyJob(const DavItem &item, QObject *parent = nullptr); + + /** + * Starts the job. + */ + void start() override; + + /** + * Returns the modified item including the updated etag information. + */ + Q_REQUIRED_RESULT DavItem item() const; + + Q_REQUIRED_RESULT QUrl itemUrl() const; + + /** + * Returns the item that triggered the conflict, if any. + */ + Q_REQUIRED_RESULT DavItem freshItem() const; + + /** + * Returns the response code we got when fetching the fresh item. + */ + Q_REQUIRED_RESULT int freshResponseCode() const; + +private: + void davJobFinished(KJob *); + void itemRefreshed(KJob *); + void conflictingItemFetched(KJob *); + DavItem mItem; + DavItem mFreshItem; + int mFreshResponseCode; +}; +} + +#endif diff --git a/src/common/davitemsfetchjob.cpp b/src/common/davitemsfetchjob.cpp new file mode 100644 index 0000000..59f7617 --- /dev/null +++ b/src/common/davitemsfetchjob.cpp @@ -0,0 +1,162 @@ +/* + Copyright (c) 2010 Grégory Oestreicher + Based on DavItemsListJob which is copyright (c) 2010 Tobias Koenig + + 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 "davitemsfetchjob.h" + +#include "davmanager.h" +#include "davmultigetprotocol.h" +#include "utils.h" +#include "daverror.h" + +#include +#include + +using namespace KDAV; + +DavItemsFetchJob::DavItemsFetchJob(const DavUrl &collectionUrl, const QStringList &urls, QObject *parent) + : DavJobBase(parent) + , mCollectionUrl(collectionUrl) + , mUrls(urls) +{ +} + +void DavItemsFetchJob::start() +{ + const DavMultigetProtocol *protocol + = dynamic_cast(DavManager::self()->davProtocol(mCollectionUrl.protocol())); + if (!protocol) { + setError(ERR_NO_MULTIGET); + setErrorTextFromDavError(); + emitResult(); + return; + } + + const QDomDocument report = protocol->itemsReportQuery(mUrls)->buildQuery(); + KIO::DavJob *job = DavManager::self()->createReportJob(mCollectionUrl.url(), report, QStringLiteral("0")); + job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); + connect(job, &KIO::DavJob::result, this, &DavItemsFetchJob::davJobFinished); +} + +DavItem::List DavItemsFetchJob::items() const +{ + DavItem::List values; + values.reserve(mItems.size()); + Q_FOREACH (const auto &value, mItems) { + values << value; + } + return values; +} + +DavItem DavItemsFetchJob::item(const QString &url) const +{ + return mItems.value(url); +} + +void DavItemsFetchJob::davJobFinished(KJob *job) +{ + KIO::DavJob *davJob = qobject_cast(job); + const QString responseCodeStr = davJob->queryMetaData(QStringLiteral("responsecode")); + const int responseCode = responseCodeStr.isEmpty() + ? 0 + : responseCodeStr.toInt(); + + // KIO::DavJob does not set error() even if the HTTP status code is a 4xx or a 5xx + if (davJob->error() || (responseCode >= 400 && responseCode < 600)) { + setLatestResponseCode(responseCode); + setError(ERR_PROBLEM_WITH_REQUEST); + setJobErrorText(davJob->errorText()); + setJobError(davJob->error()); + setErrorTextFromDavError(); + + emitResult(); + return; + } + + const DavMultigetProtocol *protocol + = static_cast(DavManager::self()->davProtocol(mCollectionUrl.protocol())); + + const QDomDocument document = davJob->response(); + const QDomElement documentElement = document.documentElement(); + + QDomElement responseElement = Utils::firstChildElementNS(documentElement, QStringLiteral("DAV:"), QStringLiteral("response")); + + while (!responseElement.isNull()) { + QDomElement propstatElement = Utils::firstChildElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("propstat")); + + if (propstatElement.isNull()) { + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + continue; + } + + // Check for errors + const QDomElement statusElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("status")); + if (!statusElement.text().contains(QLatin1String("200"))) { + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + continue; + } + + const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); + + DavItem item; + + // extract path + const QDomElement hrefElement = Utils::firstChildElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("href")); + const QString href = hrefElement.text(); + + QUrl url = davJob->url(); + if (href.startsWith(QLatin1Char('/'))) { + // href is only a path, use request url to complete + url.setPath(href, QUrl::TolerantMode); + } else { + // href is a complete url + url = QUrl::fromUserInput(href); + } + + auto _url = url; + _url.setUserInfo(mCollectionUrl.url().userInfo()); + item.setUrl(DavUrl(_url, mCollectionUrl.protocol())); + + // extract etag + const QDomElement getetagElement = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("getetag")); + item.setEtag(getetagElement.text()); + + // extract content + const QDomElement dataElement = Utils::firstChildElementNS(propElement, + protocol->responseNamespace(), + protocol->dataTagName()); + + if (dataElement.isNull()) { + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + continue; + } + + const QByteArray data = dataElement.firstChild().toText().data().toUtf8(); + if (data.isEmpty()) { + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + continue; + } + + item.setData(data); + + mItems.insert(item.url().toDisplayString(), item); + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + } + + emitResult(); +} diff --git a/src/common/davitemsfetchjob.h b/src/common/davitemsfetchjob.h new file mode 100644 index 0000000..25d3117 --- /dev/null +++ b/src/common/davitemsfetchjob.h @@ -0,0 +1,73 @@ +/* + Copyright (c) 2010 Grégory Oestreicher + Based on DavItemsListJob which is copyright (c) 2010 Tobias Koenig + + 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 KDAV_DAVITEMSFETCHJOB_H +#define KDAV_DAVITEMSFETCHJOB_H + +#include "kpimkdav_export.h" + +#include "davitem.h" +#include "davjobbase.h" +#include "davurl.h" + +#include +#include + +namespace KDAV { +/** + * @short A job that fetches a list of items from a DAV server using a multiget query. + */ +class KPIMKDAV_EXPORT DavItemsFetchJob : public DavJobBase +{ + Q_OBJECT + +public: + /** + * Creates a new items fetch job. + * + * @param collectionUrl The DAV collection on which to run the query + * @param urls The list of urls to fetch + * @param parent The parent object + */ + DavItemsFetchJob(const DavUrl &collectionUrl, const QStringList &urls, QObject *parent = nullptr); + + /** + * Starts the job. + */ + void start() override; + + /** + * Returns the list of fetched items + */ + Q_REQUIRED_RESULT DavItem::List items() const; + + /** + * Return the item found at @p url + */ + Q_REQUIRED_RESULT DavItem item(const QString &url) const; + +private: + void davJobFinished(KJob *); + DavUrl mCollectionUrl; + QStringList mUrls; + QMap mItems; +}; +} + +#endif diff --git a/src/common/davitemslistjob.cpp b/src/common/davitemslistjob.cpp new file mode 100644 index 0000000..41f3153 --- /dev/null +++ b/src/common/davitemslistjob.cpp @@ -0,0 +1,264 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 "davitemslistjob.h" + +#include "daverror.h" +#include "davmanager.h" +#include "davprotocolbase.h" +#include "davurl.h" +#include "utils.h" +#include "etagcache.h" + +#include +#include + +using namespace KDAV; + +class DavItemsListJobPrivate +{ +public: + DavItemsListJobPrivate(const DavUrl &url, const std::shared_ptr &cache); + + DavUrl mUrl; + std::shared_ptr mEtagCache; + QStringList mMimeTypes; + QString mRangeStart; + QString mRangeEnd; + DavItem::List mItems; + QSet mSeenUrls; // to prevent events duplication with some servers + DavItem::List mChangedItems; + QStringList mDeletedItems; + uint mSubJobCount; +}; + +DavItemsListJobPrivate::DavItemsListJobPrivate(const DavUrl &url, const std::shared_ptr &cache) + : mUrl(url) + , mEtagCache(cache) + , mSubJobCount(0) +{ +} + +DavItemsListJob::DavItemsListJob(const DavUrl &url, const std::shared_ptr &cache, QObject *parent) + : DavJobBase(parent) + , d(std::unique_ptr(new DavItemsListJobPrivate(url, cache))) +{ +} + +DavItemsListJob::~DavItemsListJob() +{ +} + +void DavItemsListJob::setContentMimeTypes(const QStringList &types) +{ + d->mMimeTypes = types; +} + +void DavItemsListJob::setTimeRange(const QString &start, const QString &end) +{ + d->mRangeStart = start; + d->mRangeEnd = end; +} + +void DavItemsListJob::start() +{ + const DavProtocolBase *protocol = DavManager::self()->davProtocol(d->mUrl.protocol()); + Q_ASSERT(protocol); + QVectorIterator it(protocol->itemsQueries()); + + while (it.hasNext()) { + XMLQueryBuilder::Ptr builder = it.next(); + if (!d->mRangeStart.isEmpty()) { + builder->setParameter(QStringLiteral("start"), d->mRangeStart); + } + if (!d->mRangeEnd.isEmpty()) { + builder->setParameter(QStringLiteral("end"), d->mRangeEnd); + } + + const QDomDocument props = builder->buildQuery(); + const QString mimeType = builder->mimeType(); + + if (d->mMimeTypes.isEmpty() || d->mMimeTypes.contains(mimeType)) { + ++d->mSubJobCount; + if (protocol->useReport()) { + KIO::DavJob *job = DavManager::self()->createReportJob(d->mUrl.url(), props); + job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); + job->setProperty("davType", QStringLiteral("report")); + job->setProperty("itemsMimeType", mimeType); + connect(job, &KIO::DavJob::result, this, &DavItemsListJob::davJobFinished); + } else { + KIO::DavJob *job = DavManager::self()->createPropFindJob(d->mUrl.url(), props); + job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); + job->setProperty("davType", QStringLiteral("propFind")); + job->setProperty("itemsMimeType", mimeType); + connect(job, &KIO::DavJob::result, this, &DavItemsListJob::davJobFinished); + } + } + } + + if (d->mSubJobCount == 0) { + setError(ERR_ITEMLIST_NOMIMETYPE); + setErrorTextFromDavError(); + emitResult(); + } +} + +DavItem::List DavItemsListJob::items() const +{ + return d->mItems; +} + +DavItem::List DavItemsListJob::changedItems() const +{ + return d->mChangedItems; +} + +QStringList DavItemsListJob::deletedItems() const +{ + return d->mDeletedItems; +} + +void DavItemsListJob::davJobFinished(KJob *job) +{ + KIO::DavJob *davJob = qobject_cast(job); + const int responseCode = davJob->queryMetaData(QStringLiteral("responsecode")).isEmpty() + ? 0 + : davJob->queryMetaData(QStringLiteral("responsecode")).toInt(); + + // KIO::DavJob does not set error() even if the HTTP status code is a 4xx or a 5xx + if (davJob->error() || (responseCode >= 400 && responseCode < 600)) { + setLatestResponseCode(responseCode); + setError(ERR_PROBLEM_WITH_REQUEST); + setJobErrorText(davJob->errorText()); + setJobError(davJob->error()); + setErrorTextFromDavError(); + } else { + /* + * Extract data from a document like the following: + * + * + * + * /caldav.php/test1.user/home/KOrganizer-166749289.780.ics + * + * + * "b4bbea0278f4f63854c4167a7656024a" + * + * HTTP/1.1 200 OK + * + * + * + * /caldav.php/test1.user/home/KOrganizer-399416366.464.ics + * + * + * "52eb129018398a7da4f435b2bc4c6cd5" + * + * HTTP/1.1 200 OK + * + * + * + */ + + const QString itemsMimeType = job->property("itemsMimeType").toString(); + const QDomDocument document = davJob->response(); + const QDomElement documentElement = document.documentElement(); + + QDomElement responseElement = Utils::firstChildElementNS(documentElement, QStringLiteral("DAV:"), QStringLiteral("response")); + while (!responseElement.isNull()) { + QDomElement propstatElement; + + // check for the valid propstat, without giving up on first error + { + const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat")); + for (int i = 0; i < propstats.length(); ++i) { + const QDomElement propstatCandidate = propstats.item(i).toElement(); + const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status")); + if (statusElement.text().contains(QLatin1String("200"))) { + propstatElement = propstatCandidate; + } + } + } + + if (propstatElement.isNull()) { + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + continue; + } + + const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); + + // check whether it is a dav collection ... + const QDomElement resourcetypeElement = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("resourcetype")); + if (!responseElement.isNull()) { + const QDomElement collectionElement = Utils::firstChildElementNS(resourcetypeElement, QStringLiteral("DAV:"), QStringLiteral("collection")); + if (!collectionElement.isNull()) { + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + continue; + } + } + + // ... if not it is an item + DavItem item; + item.setContentType(itemsMimeType); + + // extract path + const QDomElement hrefElement = Utils::firstChildElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("href")); + const QString href = hrefElement.text(); + + QUrl url = davJob->url(); + url.setUserInfo(QString()); + if (href.startsWith(QLatin1Char('/'))) { + // href is only a path, use request url to complete + url.setPath(href, QUrl::TolerantMode); + } else { + // href is a complete url + url = QUrl::fromUserInput(href); + } + + QString itemUrl = url.toDisplayString(); + if (d->mSeenUrls.contains(itemUrl)) { + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + continue; + } + + d->mSeenUrls << itemUrl; + auto _url = url; + _url.setUserInfo(d->mUrl.url().userInfo()); + item.setUrl(DavUrl(_url, d->mUrl.protocol())); + + // extract etag + const QDomElement getetagElement = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("getetag")); + + item.setEtag(getetagElement.text()); + + d->mItems << item; + + if (d->mEtagCache->etagChanged(itemUrl, item.etag())) { + d->mChangedItems << item; + } + + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + } + } + + QSet removed = d->mEtagCache->urls().toSet(); + removed.subtract(d->mSeenUrls); + d->mDeletedItems = removed.toList(); + + if (--d->mSubJobCount == 0) { + emitResult(); + } +} diff --git a/src/common/davitemslistjob.h b/src/common/davitemslistjob.h new file mode 100644 index 0000000..7b89e82 --- /dev/null +++ b/src/common/davitemslistjob.h @@ -0,0 +1,99 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 KDAV_DAVITEMSLISTJOB_H +#define KDAV_DAVITEMSLISTJOB_H + +#include "kpimkdav_export.h" + +#include "davitem.h" +#include "davjobbase.h" + +#include + +#include + +class DavItemsListJobPrivate; + +namespace KDAV { +class EtagCache; +class DavUrl; + +/** + * @short A job that lists all DAV items inside a DAV collection. + */ +class KPIMKDAV_EXPORT DavItemsListJob : public DavJobBase +{ + Q_OBJECT + +public: + /** + * Creates a new dav items list job. + * + * @param url The url of the DAV collection. + * @param parent The parent object. + */ + DavItemsListJob(const DavUrl &url, const std::shared_ptr &cache, QObject *parent = nullptr); + + ~DavItemsListJob() override; + + /** + * Limits the mime types of the items requested. + * + * If no mime type is given then all will be requested. + * + * @param types The list of mime types to include + */ + void setContentMimeTypes(const QStringList &types); + + /** + * Sets the start and end time to list items for. + * + * @param start The range start, in format "date with UTC time" + * @param end The range end, in format "date with UTC time" + */ + void setTimeRange(const QString &start, const QString &end); + + /** + * Starts the job. + */ + void start() override; + + /** + * Returns the list of items seen including identifier url and etag information. + */ + Q_REQUIRED_RESULT DavItem::List items() const; + + /** + * Returns the list of items that were changed on the server. + */ + Q_REQUIRED_RESULT DavItem::List changedItems() const; + + /** + * Returns the list of items URLs that were not seen in the backend. + * As this is based on the etag cache this may contain dependent items. + */ + Q_REQUIRED_RESULT QStringList deletedItems() const; + +private: + void davJobFinished(KJob *); + std::unique_ptr d; +}; +} + +#endif diff --git a/src/common/davjobbase.cpp b/src/common/davjobbase.cpp new file mode 100644 index 0000000..e93ec64 --- /dev/null +++ b/src/common/davjobbase.cpp @@ -0,0 +1,131 @@ +/* + Copyright (c) 2014 Gregory Oestreicher + + 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 "davjobbase.h" + +#include "daverror.h" + +using namespace KDAV; + +class DavJobBasePrivate +{ +public: + DavJobBasePrivate(); + + int mLatestResponseCode = 0; + int mJobErrorCode = 0; + QString mInternalErrorText; +}; + +DavJobBasePrivate::DavJobBasePrivate() +{ +} + +DavJobBase::DavJobBase(QObject *parent) + : KJob(parent) + , d(std::unique_ptr(new DavJobBasePrivate())) +{ +} + +DavJobBase::~DavJobBase() +{ +} + +int DavJobBase::latestResponseCode() const +{ + return d->mLatestResponseCode; +} + +bool DavJobBase::canRetryLater() const +{ + bool ret = false; + + // Be explicit and readable by splitting the if/else if clauses + + if (latestResponseCode() == 0 && error()) { + // Likely a timeout or a connection failure. + ret = true; + } else if (latestResponseCode() == 401) { + // Authentication required + ret = true; + } else if (latestResponseCode() == 402) { + // Payment required + ret = true; + } else if (latestResponseCode() == 407) { + // Proxy authentication required + ret = true; + } else if (latestResponseCode() == 408) { + // Request timeout + ret = true; + } else if (latestResponseCode() == 423) { + // Locked + ret = true; + } else if (latestResponseCode() == 429) { + // Too many requests + ret = true; + } else if (latestResponseCode() >= 501 && latestResponseCode() <= 504) { + // Various server-side errors + ret = true; + } else if (latestResponseCode() == 507) { + // Insufficient storage + ret = true; + } else if (latestResponseCode() == 511) { + // Network authentication required + ret = true; + } + + return ret; +} + +bool DavJobBase::hasConflict() const +{ + return latestResponseCode() == 412; +} + +void DavJobBase::setLatestResponseCode(int code) +{ + d->mLatestResponseCode = code; +} + +Error DavJobBase::davError() const +{ + return Error(static_cast(error()), d->mLatestResponseCode, d->mInternalErrorText, d->mJobErrorCode); +} + +void DavJobBase::setJobErrorText(const QString &errorText) +{ + d->mInternalErrorText = errorText; +} + +void DavJobBase::setJobError(int jobErrorCode) +{ + d->mJobErrorCode = jobErrorCode; +} + +void DavJobBase::setErrorTextFromDavError() +{ + setErrorText(davError().errorText()); +} + +void DavJobBase::setDavError(const Error &error) +{ + setError(error.errorNumber()); + setLatestResponseCode(error.responseCode()); + setJobErrorText(error.internalErrorText()); + setJobError(error.jobErrorCode()); +} diff --git a/src/common/davjobbase.h b/src/common/davjobbase.h new file mode 100644 index 0000000..2fd8e16 --- /dev/null +++ b/src/common/davjobbase.h @@ -0,0 +1,103 @@ +/* + Copyright (c) 2014 Gregory Oestreicher + + 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 KDAV_DAVJOBBASE_H +#define KDAV_DAVJOBBASE_H + +#include + +#include "kpimkdav_export.h" + +#include + +class DavJobBasePrivate; + +namespace KDAV { +class Error; + +/** + * @short base class for the jobs used by the resource. + */ +class KPIMKDAV_EXPORT DavJobBase : public KJob +{ + Q_OBJECT + +public: + explicit DavJobBase(QObject *parent = nullptr); + ~DavJobBase(); + + /** + * Get the latest response code. + * + * If no response code has been set then 0 will be returned, but will + * be meaningless unless error() is non-zero. In that case this means + * that the latest error was not at the HTTP level. + */ + Q_REQUIRED_RESULT int latestResponseCode() const; + + /** + * Check if the job can be retried later. + * + * This will return true for transient errors, i.e. if the response code + * is either zero and error() is set or if the HTTP response code hints + * at a temporary error. + * + * The HTTP response codes considered retryable are: + * - 401 + * - 402 + * - 407 + * - 408 + * - 423 + * - 429 + * - 501 to 504, inclusive + * - 507 + * - 511 + */ + Q_REQUIRED_RESULT bool canRetryLater() const; + + /** + * Check if the job failed because of a conflict + */ + Q_REQUIRED_RESULT bool hasConflict() const; + + /** + * Returns a instance of the KDAV:Error to be able to translate the error + */ + Q_REQUIRED_RESULT Error davError() const; + +protected: + /** + * Sets the latest response code received. + * + * Only really useful in case of error, though success codes can + * also be set. + * + * @param code The code to set, should be a valid HTTP response code or zero. + */ + void setLatestResponseCode(int code); + + void setJobErrorText(const QString &errorText); + void setJobError(int jobErrorCode); + void setErrorTextFromDavError(); + void setDavError(const Error &error); +private: + std::unique_ptr d; +}; +} + +#endif diff --git a/src/common/davmanager.cpp b/src/common/davmanager.cpp new file mode 100644 index 0000000..11cc145 --- /dev/null +++ b/src/common/davmanager.cpp @@ -0,0 +1,124 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 "davmanager.h" + +#include "protocols/caldavprotocol.h" +#include "protocols/carddavprotocol.h" +#include "protocols/groupdavprotocol.h" + +#include + +#include "libkdav_debug.h" + +#include +#include + +using namespace KDAV; + +DavManager *DavManager::mSelf = nullptr; + +DavManager::DavManager() +{ +} + +DavManager::~DavManager() +{ + QMapIterator it(mProtocols); + while (it.hasNext()) { + it.next(); + delete it.value(); + } +} + +DavManager *DavManager::self() +{ + if (!mSelf) { + mSelf = new DavManager(); + } + + return mSelf; +} + +KIO::DavJob *DavManager::createPropFindJob(const QUrl &url, const QDomDocument &document, const QString &depth) const +{ + KIO::DavJob *job = KIO::davPropFind(url, document, depth, KIO::HideProgressInfo | KIO::DefaultFlags); + + // workaround needed, Depth: header doesn't seem to be correctly added + const QString header = QLatin1String("Content-Type: text/xml\r\nDepth: ") + depth; + job->addMetaData(QStringLiteral("customHTTPHeader"), header); + job->addMetaData(QStringLiteral("cookies"), QStringLiteral("none")); + job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); + job->setProperty("extraDavDepth", QVariant::fromValue(depth)); + + return job; +} + +KIO::DavJob *DavManager::createReportJob(const QUrl &url, const QDomDocument &document, const QString &depth) const +{ + KIO::DavJob *job = KIO::davReport(url, document.toString(), depth, KIO::HideProgressInfo | KIO::DefaultFlags); + + // workaround needed, Depth: header doesn't seem to be correctly added + const QString header = QLatin1String("Content-Type: text/xml\r\nDepth: ") + depth; + job->addMetaData(QStringLiteral("customHTTPHeader"), header); + job->addMetaData(QStringLiteral("cookies"), QStringLiteral("none")); + job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); + job->setProperty("extraDavDepth", QVariant::fromValue(depth)); + + return job; +} + +KIO::DavJob *DavManager::createPropPatchJob(const QUrl &url, const QDomDocument &document) const +{ + KIO::DavJob *job = KIO::davPropPatch(url, document, KIO::HideProgressInfo | KIO::DefaultFlags); + const QString header = QStringLiteral("Content-Type: text/xml"); + job->addMetaData(QStringLiteral("customHTTPHeader"), header); + job->addMetaData(QStringLiteral("cookies"), QStringLiteral("none")); + job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); + return job; +} + +const DavProtocolBase *DavManager::davProtocol(Protocol protocol) +{ + if (createProtocol(protocol)) { + return mProtocols[ protocol ]; + } else { + return nullptr; + } +} + +bool DavManager::createProtocol(Protocol protocol) +{ + if (mProtocols.contains(protocol)) { + return true; + } + + switch (protocol) { + case KDAV::CalDav: + mProtocols.insert(KDAV::CalDav, new CaldavProtocol()); + return true; + case KDAV::CardDav: + mProtocols.insert(KDAV::CardDav, new CarddavProtocol()); + return true; + case KDAV::GroupDav: + mProtocols.insert(KDAV::GroupDav, new GroupdavProtocol()); + return true; + } + qCCritical(KDAV_LOG) << "Unknown protocol: " << static_cast(protocol); + return false; +} diff --git a/src/common/davmanager.h b/src/common/davmanager.h new file mode 100644 index 0000000..b42f263 --- /dev/null +++ b/src/common/davmanager.h @@ -0,0 +1,108 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 KDAV_DAVMANAGER_H +#define KDAV_DAVMANAGER_H + +#include "kpimkdav_export.h" + +#include "enums.h" + +#include +#include + +namespace KIO { +class DavJob; +} + +class QUrl; + +class QDomDocument; + +namespace KDAV { +class DavProtocolBase; + +/** + * @short A factory class for handling DAV jobs. + * + * This class provides factory methods to create preconfigured + * low-level DAV jobs and has access to the global DAV protocol dialect + * objects which abstract the access to the various DAV protocol dialects. + */ +class KPIMKDAV_EXPORT DavManager +{ +public: + /** + * Destroys the DAV manager. + */ + ~DavManager(); + + /** + * Returns the global instance of the DAV manager. + */ + static DavManager *self(); + + /** + * Returns a preconfigured DAV PROPFIND job. + * + * @param url The target url of the job. + * @param document The query XML document. + * @param depth The Depth: value to send in the HTTP request + */ + KIO::DavJob *createPropFindJob(const QUrl &url, const QDomDocument &document, const QString &depth = QStringLiteral("1")) const; + + /** + * Returns a preconfigured DAV REPORT job. + * + * @param url The target url of the job. + * @param document The query XML document. + * @param depth The Depth: value to send in the HTTP request + */ + KIO::DavJob *createReportJob(const QUrl &url, const QDomDocument &document, const QString &depth = QStringLiteral("1")) const; + + /** + * Returns a preconfigured DAV PROPPATCH job. + * + * @param url The target url of the job. + * @param document The query XML document. + */ + KIO::DavJob *createPropPatchJob(const QUrl &url, const QDomDocument &document) const; + + /** + * Returns the DAV protocol dialect object for the given DAV @p protocol. + */ + const DavProtocolBase *davProtocol(Protocol protocol); + +private: + /** + * Creates a new DAV manager. + */ + DavManager(); + + /** + * Creates a new protocol. + */ + bool createProtocol(Protocol protocol); + + typedef QMap protocolsMap; + protocolsMap mProtocols; + static DavManager *mSelf; +}; +} + +#endif diff --git a/src/common/davmultigetprotocol.cpp b/src/common/davmultigetprotocol.cpp new file mode 100644 index 0000000..0704b5d --- /dev/null +++ b/src/common/davmultigetprotocol.cpp @@ -0,0 +1,25 @@ +/* + Copyright (c) 2010 Grégory Oestreicher + + 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 "davmultigetprotocol.h" + +using namespace KDAV; + +DavMultigetProtocol::~DavMultigetProtocol() +{ +} diff --git a/src/common/davmultigetprotocol.h b/src/common/davmultigetprotocol.h new file mode 100644 index 0000000..240b4f8 --- /dev/null +++ b/src/common/davmultigetprotocol.h @@ -0,0 +1,56 @@ +/* + Copyright (c) 2010 Grégory Oestreicher + + 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 KDAV_DAVMULTIGETPROTOCOL_H +#define KDAV_DAVMULTIGETPROTOCOL_H + +#include "kpimkdav_export.h" + +#include "davprotocolbase.h" + +namespace KDAV { +/** + * @short Base class for protocols that implement multiget capabilities + */ +class KPIMKDAV_EXPORT DavMultigetProtocol : public DavProtocolBase +{ +public: + /** + * Destroys the DAV protocol + */ + virtual ~DavMultigetProtocol(); + + /** + * Returns the XML document that represents a MULTIGET DAV query to + * list all DAV resources with the given @p urls. + */ + virtual XMLQueryBuilder::Ptr itemsReportQuery(const QStringList &urls) const = 0; + + /** + * Returns the namespace used by protocol-specific elements found in responses. + */ + virtual QString responseNamespace() const = 0; + + /** + * Returns the tag name of data elements found in responses. + */ + virtual QString dataTagName() const = 0; +}; +} + +#endif diff --git a/src/common/davprincipalhomesetsfetchjob.cpp b/src/common/davprincipalhomesetsfetchjob.cpp new file mode 100644 index 0000000..95e6377 --- /dev/null +++ b/src/common/davprincipalhomesetsfetchjob.cpp @@ -0,0 +1,228 @@ +/* + Copyright (c) 2010 Grégory Oestreicher + + 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 "davprincipalhomesetsfetchjob.h" + +#include "davmanager.h" +#include "davprotocolbase.h" +#include "daverror.h" +#include "utils.h" + +#include +#include + +using namespace KDAV; + +DavPrincipalHomeSetsFetchJob::DavPrincipalHomeSetsFetchJob(const DavUrl &url, QObject *parent) + : DavJobBase(parent) + , mUrl(url) +{ +} + +void DavPrincipalHomeSetsFetchJob::start() +{ + fetchHomeSets(false); +} + +void DavPrincipalHomeSetsFetchJob::fetchHomeSets(bool homeSetsOnly) +{ + QDomDocument document; + + QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); + document.appendChild(propfindElement); + + QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + propfindElement.appendChild(propElement); + + const QString homeSet = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSet(); + const QString homeSetNS = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSetNS(); + propElement.appendChild(document.createElementNS(homeSetNS, homeSet)); + + if (!homeSetsOnly) { + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("current-user-principal"))); + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("principal-URL"))); + } + + KIO::DavJob *job = DavManager::self()->createPropFindJob(mUrl.url(), document, QStringLiteral("0")); + job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); + connect(job, &KIO::DavJob::result, this, &DavPrincipalHomeSetsFetchJob::davJobFinished); +} + +QStringList DavPrincipalHomeSetsFetchJob::homeSets() const +{ + return mHomeSets; +} + +void DavPrincipalHomeSetsFetchJob::davJobFinished(KJob *job) +{ + KIO::DavJob *davJob = qobject_cast(job); + const QString responseCodeStr = davJob->queryMetaData(QStringLiteral("responsecode")); + const int responseCode = responseCodeStr.isEmpty() + ? 0 + : responseCodeStr.toInt(); + + // KIO::DavJob does not set error() even if the HTTP status code is a 4xx or a 5xx + if (davJob->error() || (responseCode >= 400 && responseCode < 600)) { + QString err; + if (davJob->error() && davJob->error() != KIO::ERR_SLAVE_DEFINED) { + err = KIO::buildErrorString(davJob->error(), davJob->errorText()); + } else { + err = davJob->errorText(); + } + + setLatestResponseCode(responseCode); + setError(ERR_PROBLEM_WITH_REQUEST); + setJobErrorText(davJob->errorText()); + setJobError(davJob->error()); + setErrorTextFromDavError(); + + emitResult(); + return; + } + + /* + * Extract information from a document like the following (if no homeset is defined) : + * + * + * + * /dav/ + * + * HTTP/1.1 200 OK + * + * + * /principals/users/gdacoin/ + * + * + * + * + * HTTP/1.1 404 Not Found + * + * + * + * + * + * + * + * + * Or like this (if the homeset is defined): + * + * + * + * + * /principals/users/greg%40kamago.net/ + * + * + * + * /greg%40kamago.net/ + * + * + * HTTP/1.1 200 OK + * + * + * + */ + + const QString homeSet = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSet(); + const QString homeSetNS = DavManager::self()->davProtocol(mUrl.protocol())->principalHomeSetNS(); + QString nextRoundHref; // The content of the href element that will be used if no homeset was found. + // This is either given by current-user-principal or by principal-URL. + + const QDomDocument document = davJob->response(); + const QDomElement multistatusElement = document.documentElement(); + + QDomElement responseElement = Utils::firstChildElementNS(multistatusElement, QStringLiteral("DAV:"), QStringLiteral("response")); + while (!responseElement.isNull()) { + QDomElement propstatElement; + + // check for the valid propstat, without giving up on first error + { + const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat")); + for (int i = 0; i < propstats.length(); ++i) { + const QDomElement propstatCandidate = propstats.item(i).toElement(); + const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status")); + if (statusElement.text().contains(QLatin1String("200"))) { + propstatElement = propstatCandidate; + } + } + } + + if (propstatElement.isNull()) { + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + continue; + } + + // extract home sets + const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); + const QDomElement homeSetElement = Utils::firstChildElementNS(propElement, homeSetNS, homeSet); + + if (!homeSetElement.isNull()) { + QDomElement hrefElement = Utils::firstChildElementNS(homeSetElement, QStringLiteral("DAV:"), QStringLiteral("href")); + + while (!hrefElement.isNull()) { + const QString href = hrefElement.text(); + if (!mHomeSets.contains(href)) { + mHomeSets << href; + } + + hrefElement = Utils::nextSiblingElementNS(hrefElement, QStringLiteral("DAV:"), QStringLiteral("href")); + } + } else { + // Trying to get the principal url, given either by current-user-principal or principal-URL + QDomElement urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("current-user-principal")); + if (urlHolder.isNull()) { + urlHolder = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("principal-URL")); + } + + if (!urlHolder.isNull()) { + // Getting the href that will be used for the next round + QDomElement hrefElement = Utils::firstChildElementNS(urlHolder, QStringLiteral("DAV:"), QStringLiteral("href")); + if (!hrefElement.isNull()) { + nextRoundHref = hrefElement.text(); + } + } + } + + responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); + } + + /* + * Now either we got one or more homesets, or we got an href for the next round + * or nothing can be found by this job. + * If we have homesets, we're done here and can notify the caller. + * Else we must ensure that we have an href for the next round. + */ + if (!mHomeSets.isEmpty() || nextRoundHref.isEmpty()) { + emitResult(); + } else { + QUrl nextRoundUrl(mUrl.url()); + + if (nextRoundHref.startsWith(QLatin1Char('/'))) { + // nextRoundHref is only a path, use request url to complete + nextRoundUrl.setPath(nextRoundHref, QUrl::TolerantMode); + } else { + // href is a complete url + nextRoundUrl = QUrl::fromUserInput(nextRoundHref); + nextRoundUrl.setUserName(mUrl.url().userName()); + nextRoundUrl.setPassword(mUrl.url().password()); + } + + mUrl.setUrl(nextRoundUrl); + // And one more round, fetching only homesets + fetchHomeSets(true); + } +} diff --git a/src/common/davprincipalhomesetsfetchjob.h b/src/common/davprincipalhomesetsfetchjob.h new file mode 100644 index 0000000..6e95177 --- /dev/null +++ b/src/common/davprincipalhomesetsfetchjob.h @@ -0,0 +1,79 @@ +/* + Copyright (c) 2010 Grégory Oestreicher + + 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 KDAV_DAVPRINCIPALHOMESETSFETCHJOB_H +#define KDAV_DAVPRINCIPALHOMESETSFETCHJOB_H + +#include "kpimkdav_export.h" + +#include "davjobbase.h" +#include "davurl.h" + +#include + +#include + +namespace KDAV { +/** + * @short A job that fetches home sets for a principal. + */ +class KPIMKDAV_EXPORT DavPrincipalHomeSetsFetchJob : public DavJobBase +{ + Q_OBJECT + +public: + /** + * Creates a new dav principals home sets fetch job. + * + * @param url The DAV url of the DAV principal. + * @param parent The parent object. + */ + explicit DavPrincipalHomeSetsFetchJob(const DavUrl &url, QObject *parent = nullptr); + + /** + * Starts the job. + */ + void start() override; + + /** + * Returns the found home sets. + */ + Q_REQUIRED_RESULT QStringList homeSets() const; + +private: + void davJobFinished(KJob *); + /** + * Start the fetch process. + * + * There may be two rounds necessary if the first request + * does not returns the home sets, but only the current-user-principal + * or the principal-URL. The bool flag is here to prevent requesting + * those last two on each request, as they are only fetched in + * the first round. + * + * @param fetchHomeSetsOnly If set to true the request will not include + * the current-user-principal and principal-URL props. + */ + void fetchHomeSets(bool fetchHomeSetsOnly); + + DavUrl mUrl; + QStringList mHomeSets; +}; +} + +#endif diff --git a/src/common/davprincipalsearchjob.cpp b/src/common/davprincipalsearchjob.cpp new file mode 100644 index 0000000..97d4e8b --- /dev/null +++ b/src/common/davprincipalsearchjob.cpp @@ -0,0 +1,376 @@ +/* + Copyright (c) 2011 Grégory Oestreicher + + 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 "davprincipalsearchjob.h" + +#include "davmanager.h" +#include "utils.h" +#include "daverror.h" + +#include +#include + +#include + +using namespace KDAV; + +DavPrincipalSearchJob::DavPrincipalSearchJob(const DavUrl &url, DavPrincipalSearchJob::FilterType type, const QString &filter, QObject *parent) + : DavJobBase(parent) + , mUrl(url) + , mType(type) + , mFilter(filter) + , mPrincipalPropertySearchSubJobCount(0) + , mPrincipalPropertySearchSubJobSuccessful(false) +{ +} + +void DavPrincipalSearchJob::fetchProperty(const QString &name, const QString &ns) +{ + QString propNamespace = ns; + if (propNamespace.isEmpty()) { + propNamespace = QStringLiteral("DAV:"); + } + + mFetchProperties << QPair(propNamespace, name); +} + +DavUrl DavPrincipalSearchJob::davUrl() const +{ + return mUrl; +} + +void DavPrincipalSearchJob::start() +{ + /* + * The first step is to try to locate the URL that contains the principals. + * This is done with a PROPFIND request and a XML like this: + * + * + * + * + * + * + */ + QDomDocument query; + + QDomElement propfind = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); + query.appendChild(propfind); + + QDomElement prop = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + propfind.appendChild(prop); + + QDomElement principalCollectionSet = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("principal-collection-set")); + prop.appendChild(principalCollectionSet); + + KIO::DavJob *job = DavManager::self()->createPropFindJob(mUrl.url(), query); + job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); + connect(job, &KIO::DavJob::result, this, &DavPrincipalSearchJob::principalCollectionSetSearchFinished); + job->start(); +} + +void DavPrincipalSearchJob::principalCollectionSetSearchFinished(KJob *job) +{ + KIO::DavJob *davJob = qobject_cast(job); + const QString responseCodeStr = davJob->queryMetaData(QStringLiteral("responsecode")); + const int responseCode = responseCodeStr.isEmpty() + ? 0 + : responseCodeStr.toInt(); + // KIO::DavJob does not set error() even if the HTTP status code is a 4xx or a 5xx + if (davJob->error() || (responseCode >= 400 && responseCode < 600)) { + setLatestResponseCode(responseCode); + setError(ERR_PROBLEM_WITH_REQUEST); + setJobErrorText(davJob->errorText()); + setJobError(davJob->error()); + setErrorTextFromDavError(); + + emitResult(); + return; + } + + if (job->error()) { + setError(job->error()); + setErrorText(job->errorText()); + emitResult(); + return; + } + + /* + * Extract information from a document like the following: + * + * + * + * + * http://www.example.com/papers/ + * + * + * + * http://www.example.com/acl/users/ + * http://www.example.com/acl/groups/ + * + * + * HTTP/1.1 200 OK + * + * + * + */ + + QDomDocument document = davJob->response(); + QDomElement documentElement = document.documentElement(); + + QDomElement responseElement = Utils::firstChildElementNS(documentElement, QStringLiteral("DAV:"), QStringLiteral("response")); + if (responseElement.isNull()) { + emitResult(); + return; + } + + // check for the valid propstat, without giving up on first error + QDomElement propstatElement; + { + const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat")); + for (int i = 0; i < propstats.length(); ++i) { + const QDomElement propstatCandidate = propstats.item(i).toElement(); + const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status")); + if (statusElement.text().contains(QLatin1String("200"))) { + propstatElement = propstatCandidate; + } + } + } + + if (propstatElement.isNull()) { + emitResult(); + return; + } + + QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); + if (propElement.isNull()) { + emitResult(); + return; + } + + QDomElement principalCollectionSetElement = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("principal-collection-set")); + if (principalCollectionSetElement.isNull()) { + emitResult(); + return; + } + + QDomNodeList hrefNodes = principalCollectionSetElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("href")); + for (int i = 0; i < hrefNodes.size(); ++i) { + QDomElement hrefElement = hrefNodes.at(i).toElement(); + QString href = hrefElement.text(); + + QUrl url = mUrl.url(); + if (href.startsWith(QLatin1Char('/'))) { + // href is only a path, use request url to complete + url.setPath(href, QUrl::TolerantMode); + } else { + // href is a complete url + QUrl tmpUrl(href); + tmpUrl.setUserName(url.userName()); + tmpUrl.setPassword(url.password()); + url = tmpUrl; + } + + QDomDocument principalPropertySearchQuery; + buildReportQuery(principalPropertySearchQuery); + KIO::DavJob *reportJob = DavManager::self()->createReportJob(url, principalPropertySearchQuery); + reportJob->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); + connect(reportJob, &KIO::DavJob::result, this, &DavPrincipalSearchJob::principalPropertySearchFinished); + ++mPrincipalPropertySearchSubJobCount; + reportJob->start(); + } +} + +void DavPrincipalSearchJob::principalPropertySearchFinished(KJob *job) +{ + --mPrincipalPropertySearchSubJobCount; + + if (job->error() && !mPrincipalPropertySearchSubJobSuccessful) { + setError(job->error()); + setErrorText(job->errorText()); + if (mPrincipalPropertySearchSubJobCount == 0) { + emitResult(); + } + return; + } + + KIO::DavJob *davJob = qobject_cast(job); + + const int responseCode = davJob->queryMetaData(QStringLiteral("responsecode")).toInt(); + + if (responseCode > 499 && responseCode < 600 && !mPrincipalPropertySearchSubJobSuccessful) { + // Server-side error, unrecoverable + setLatestResponseCode(responseCode); + setError(ERR_SERVER_UNRECOVERABLE); + setJobErrorText(davJob->errorText()); + setJobError(davJob->error()); + setErrorTextFromDavError(); + if (mPrincipalPropertySearchSubJobCount == 0) { + emitResult(); + } + return; + } else if (responseCode > 399 && responseCode < 500 && !mPrincipalPropertySearchSubJobSuccessful) { + setLatestResponseCode(responseCode); + setError(ERR_PROBLEM_WITH_REQUEST); + setJobErrorText(davJob->errorText()); + setJobError(davJob->error()); + setErrorTextFromDavError(); + + if (mPrincipalPropertySearchSubJobCount == 0) { + emitResult(); + } + return; + } + + if (!mPrincipalPropertySearchSubJobSuccessful) { + setError(0); // nope, everything went fine + mPrincipalPropertySearchSubJobSuccessful = true; + } + + /* + * Extract infos from a document like the following: + * + * + * + * http://www.example.com/users/jdoe + * + * + * John Doe + * + * HTTP/1.1 200 OK + * + * + */ + + const QDomDocument document = davJob->response(); + const QDomElement documentElement = document.documentElement(); + + QDomElement responseElement = Utils::firstChildElementNS(documentElement, QStringLiteral("DAV:"), QStringLiteral("response")); + if (responseElement.isNull()) { + if (mPrincipalPropertySearchSubJobCount == 0) { + emitResult(); + } + return; + } + + // check for the valid propstat, without giving up on first error + QDomElement propstatElement; + { + const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat")); + const int propStatsEnd(propstats.length()); + for (int i = 0; i < propStatsEnd; ++i) { + const QDomElement propstatCandidate = propstats.item(i).toElement(); + const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status")); + if (statusElement.text().contains(QLatin1String("200"))) { + propstatElement = propstatCandidate; + } + } + } + + if (propstatElement.isNull()) { + if (mPrincipalPropertySearchSubJobCount == 0) { + emitResult(); + } + return; + } + + QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); + if (propElement.isNull()) { + if (mPrincipalPropertySearchSubJobCount == 0) { + emitResult(); + } + return; + } + + // All requested properties are now under propElement, so let's find them + typedef QPair PropertyPair; + foreach (const PropertyPair &fetchProperty, mFetchProperties) { + QDomNodeList fetchNodes = propElement.elementsByTagNameNS(fetchProperty.first, fetchProperty.second); + for (int i = 0; i < fetchNodes.size(); ++i) { + QDomElement fetchElement = fetchNodes.at(i).toElement(); + Result result; + result.propertyNamespace = fetchProperty.first; + result.property = fetchProperty.second; + result.value = fetchElement.text(); + mResults << result; + } + } + + if (mPrincipalPropertySearchSubJobCount == 0) { + emitResult(); + } +} + +QVector< DavPrincipalSearchJob::Result > DavPrincipalSearchJob::results() const +{ + return mResults; +} + +void DavPrincipalSearchJob::buildReportQuery(QDomDocument &query) +{ + /* + * Build a document like the following, where XXX will + * be replaced by the properties the user want to fetch: + * + * + * + * + * + * + * + * FILTER + * + * + * XXX + * + * + */ + + QDomElement principalPropertySearch = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("principal-property-search")); + query.appendChild(principalPropertySearch); + + QDomElement propertySearch = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("property-search")); + principalPropertySearch.appendChild(propertySearch); + + QDomElement prop = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + propertySearch.appendChild(prop); + + if (mType == DisplayName) { + QDomElement displayName = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname")); + prop.appendChild(displayName); + } else if (mType == EmailAddress) { + QDomElement calendarUserAddressSet = query.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-user-address-set")); + prop.appendChild(calendarUserAddressSet); + //QDomElement hrefElement = query.createElementNS( "DAV:", "href" ); + //prop.appendChild( hrefElement ); + } + + QDomElement match = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("match")); + propertySearch.appendChild(match); + + QDomText propFilter = query.createTextNode(mFilter); + match.appendChild(propFilter); + + prop = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + principalPropertySearch.appendChild(prop); + + typedef QPair PropertyPair; + foreach (const PropertyPair &fetchProperty, mFetchProperties) { + QDomElement elem = query.createElementNS(fetchProperty.first, fetchProperty.second); + prop.appendChild(elem); + } +} diff --git a/src/common/davprincipalsearchjob.h b/src/common/davprincipalsearchjob.h new file mode 100644 index 0000000..2688fc2 --- /dev/null +++ b/src/common/davprincipalsearchjob.h @@ -0,0 +1,118 @@ +/* + Copyright (c) 2011 Grégory Oestreicher + + 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 KDAV_DAVPRINCIPALSEARCHJOB_H +#define KDAV_DAVPRINCIPALSEARCHJOB_H + +#include "kpimkdav_export.h" + +#include "davjobbase.h" +#include "davurl.h" + +#include +#include +#include + +#include + +class QDomDocument; + +namespace KDAV { +/** + * @short A job that search a DAV principal on a server + * + * This job is used to search a principal on a server + * that implement the dav-property-search REPORT (RFC3744). + * + * The properties to fetch are set with @ref fetchProperty(). + */ +class KPIMKDAV_EXPORT DavPrincipalSearchJob : public DavJobBase +{ + Q_OBJECT + +public: + /** + * Types of search that are supported by this job. + * DisplayName will match on the DAV displayname property. + * EmailAddress will match on the CalDav calendar-user-address-set property. + */ + enum FilterType { + DisplayName, + EmailAddress + }; + + /** + * Simple struct to hold the search job results + */ + struct Result { + QString propertyNamespace; + QString property; + QString value; + }; + + /** + * Creates a new dav principal search job + * + * @param url The URL to use in the REPORT query. + * @param type The type that the filter will match. + * @param filter The filter that will be used to match the displayname attribute. + * @param parent The parent object. + */ + explicit DavPrincipalSearchJob(const DavUrl &url, FilterType type, const QString &filter, QObject *parent = nullptr); + + /** + * Add a new property to fetch from the server. + * + * @param name The name of the property. + * @param ns The namespace of this property, defaults to 'DAV:'. + */ + void fetchProperty(const QString &name, const QString &ns = QString()); + + /** + * Starts the job + */ + void start() override; + + /** + * Return the DavUrl used by this job + */ + Q_REQUIRED_RESULT DavUrl davUrl() const; + + /** + * Get the job results. + */ + Q_REQUIRED_RESULT QVector results() const; + +private: + void principalCollectionSetSearchFinished(KJob *job); + void principalPropertySearchFinished(KJob *job); + void buildReportQuery(QDomDocument &query); + +private: + DavUrl mUrl; + FilterType mType; + QString mFilter; + int mPrincipalPropertySearchSubJobCount; + bool mPrincipalPropertySearchSubJobSuccessful; + QList< QPair > mFetchProperties; + QVector mResults; +}; +} + +Q_DECLARE_TYPEINFO(KDAV::DavPrincipalSearchJob::Result, Q_MOVABLE_TYPE); +#endif diff --git a/src/common/davprotocolbase.cpp b/src/common/davprotocolbase.cpp new file mode 100644 index 0000000..fdee204 --- /dev/null +++ b/src/common/davprotocolbase.cpp @@ -0,0 +1,55 @@ +/* + Copyright (c) 2009 Grégory Oestreicher + + 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 "davprotocolbase.h" + +#include + +using namespace KDAV; + +XMLQueryBuilder::~XMLQueryBuilder() +{ +} + +void XMLQueryBuilder::setParameter(const QString &key, const QVariant &value) +{ + mParameters[key] = value; +} + +QVariant XMLQueryBuilder::parameter(const QString &key) const +{ + QVariant ret; + if (mParameters.contains(key)) { + ret = mParameters.value(key); + } + return ret; +} + +DavProtocolBase::~DavProtocolBase() +{ +} + +QString DavProtocolBase::principalHomeSet() const +{ + return QString(); +} + +QString DavProtocolBase::principalHomeSetNS() const +{ + return QString(); +} diff --git a/src/common/davprotocolbase.h b/src/common/davprotocolbase.h new file mode 100644 index 0000000..a0dc77f --- /dev/null +++ b/src/common/davprotocolbase.h @@ -0,0 +1,135 @@ +/* + Copyright (c) 2009 Grégory Oestreicher + + 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 KDAV_DAVPROTOCOLBASE_H +#define KDAV_DAVPROTOCOLBASE_H + +#include "kpimkdav_export.h" + +#include "davcollection.h" + +#include +#include +#include +#include + +namespace KDAV { +/** + * @short Base class for XML query builders + */ +class KPIMKDAV_EXPORT XMLQueryBuilder +{ +public: + typedef QSharedPointer Ptr; + + virtual ~XMLQueryBuilder(); + + virtual QDomDocument buildQuery() const = 0; + virtual QString mimeType() const = 0; + + void setParameter(const QString &key, const QVariant &value); + QVariant parameter(const QString &key) const; + +private: + QMap mParameters; +}; + +/** + * @short Base class for various DAV groupware dialects. + * + * This class provides an interface to query the DAV dialect + * specific features and abstract them. + * + * The functionality is implemented in: + * @li CaldavProtocol + * @li CarddavProtocol + * @li GroupdavProtocol + */ +class KPIMKDAV_EXPORT DavProtocolBase +{ +public: + /** + * Destroys the dav protocol base. + */ + virtual ~DavProtocolBase(); + + /** + * Returns whether the dav protocol dialect supports principal + * queries. If true, it must return the home set it provides + * access to with principalHomeSet() and the home set namespace + * with principalHomeSetNS(); + */ + virtual bool supportsPrincipals() const = 0; + + /** + * Returns whether the dav protocol dialect supports the REPORT + * command to query all resources of a collection. + * If not, PROPFIND command will be used instead. + */ + virtual bool useReport() const = 0; + + /** + * Returns whether the dav protocol dialect supports the MULTIGET command. + * + * If MULTIGET is supported, the content of all dav resources + * can be fetched in ResourceBase::retrieveItems() already and + * there is no need to call ResourceBase::retrieveItem() for every single + * dav resource. + * + * Protocols that have MULTIGET capabilities must inherit from + * DavMultigetProtocol instead of this class. + */ + virtual bool useMultiget() const = 0; + + /** + * Returns the home set that this protocol supports. + */ + virtual QString principalHomeSet() const; + + /** + * Returns the namespace of the home set. + */ + virtual QString principalHomeSetNS() const; + + /** + * Returns the XML document that represents the DAV query to + * list all available DAV collections. + */ + virtual XMLQueryBuilder::Ptr collectionsQuery() const = 0; + + /** + * Returns the XQuery string that filters out the relevant XML elements + * from the result returned by the query that is provided by collectionQuery(). + */ + virtual QString collectionsXQuery() const = 0; + + /** + * Returns a list of XML documents that represent DAV queries to + * list all available DAV resources inside a specific DAV collection. + */ + virtual QVector itemsQueries() const = 0; + + /** + * Returns the possible content types for the collection that + * is described by the passed @p propstat element of a PROPFIND result. + */ + virtual DavCollection::ContentTypes collectionContentTypes(const QDomElement &propstat) const = 0; +}; +} + +#endif diff --git a/src/common/davurl.cpp b/src/common/davurl.cpp new file mode 100644 index 0000000..3d7eee4 --- /dev/null +++ b/src/common/davurl.cpp @@ -0,0 +1,82 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 "davurl.h" + +#include "enums.h" + +using namespace KDAV; + +DavUrl::DavUrl() + : mProtocol(KDAV::CalDav) +{ +} + +DavUrl::DavUrl(const QUrl &url, Protocol protocol) + : mUrl(url) + , mProtocol(protocol) +{ +} + +void DavUrl::setUrl(const QUrl &url) +{ + mUrl = url; +} + +QUrl DavUrl::url() const +{ + return mUrl; +} + +void DavUrl::setProtocol(Protocol protocol) +{ + mProtocol = protocol; +} + +Protocol DavUrl::protocol() const +{ + return mProtocol; +} + +QString DavUrl::toDisplayString() const +{ + auto url = mUrl; + url.setUserInfo(QString()); + return url.toDisplayString(); +} + +QDataStream &KDAV::operator<<(QDataStream &stream, const DavUrl &url) +{ + stream << QString::number(url.protocol()); + stream << url.url(); + + return stream; +} + +QDataStream &KDAV::operator>>(QDataStream &stream, DavUrl &davUrl) +{ + QUrl url; + QString p; + + stream >> p; + stream >> url; + + davUrl = DavUrl(url, static_cast(p.toInt())); + + return stream; +} diff --git a/src/common/davurl.h b/src/common/davurl.h new file mode 100644 index 0000000..4971fb2 --- /dev/null +++ b/src/common/davurl.h @@ -0,0 +1,90 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 KDAV_DAVURL_H +#define KDAV_DAVURL_H + +#include "kpimkdav_export.h" + +#include "enums.h" + +#include +#include + +namespace KDAV { +/** + * @short A helper class to combine url and protocol of a DAV url. + */ +class KPIMKDAV_EXPORT DavUrl +{ +public: + /** + * Defines a list of DAV url objects. + */ + typedef QVector List; + + /** + * Creates an empty DAV url. + */ + DavUrl(); + + /** + * Creates a new DAV url. + * + * @param url The url that identifies the DAV object. + * @param protocol The DAV protocol dialect that is used to retrieve the DAV object. + */ + DavUrl(const QUrl &url, Protocol protocol); + + /** + * Sets the @p url that identifies the DAV object. + */ + void setUrl(const QUrl &url); + + /** + * Returns the url that identifies the DAV object. + */ + Q_REQUIRED_RESULT QUrl url() const; + + /** + * Returns the url in a userfriendly way without login informations. + */ + Q_REQUIRED_RESULT QString toDisplayString() const; + + /** + * Sets the DAV @p protocol dialect that is used to retrieve the DAV object. + */ + void setProtocol(Protocol protocol); + + /** + * Returns the DAV protocol dialect that is used to retrieve the DAV object. + */ + Q_REQUIRED_RESULT Protocol protocol() const; + +private: + QUrl mUrl; + Protocol mProtocol; +}; + +KPIMKDAV_EXPORT QDataStream &operator<<(QDataStream &out, const DavUrl &url); +KPIMKDAV_EXPORT QDataStream &operator>>(QDataStream &in, DavUrl &url); +} + +Q_DECLARE_TYPEINFO(KDAV::DavUrl, Q_MOVABLE_TYPE); + +#endif diff --git a/src/common/enums.h b/src/common/enums.h new file mode 100644 index 0000000..2344c40 --- /dev/null +++ b/src/common/enums.h @@ -0,0 +1,55 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 KDAV_ENUMS_H +#define KDAV_ENUMS_H + +#include + +namespace KDAV { +/** + * Describes the DAV protocol dialect. + */ +enum Protocol { + CalDav = 0, ///< The CalDav protocol as defined in http://caldav.calconnect.org + CardDav, ///< The CardDav protocol as defined in http://carddav.calconnect.org + GroupDav ///< The GroupDav protocol as defined in http://www.groupdav.org +}; + +/** + * Describes the DAV privileges on a resource (see RFC3744) + */ +enum Privilege { + None = 0x0, + Read = 0x1, + Write = 0x2, + WriteProperties = 0x4, + WriteContent = 0x8, + Unlock = 0x10, + ReadAcl = 0x20, + ReadCurrentUserPrivilegeSet = 0x40, + WriteAcl = 0x80, + Bind = 0x100, + Unbind = 0x200, + All = 0x400 +}; +Q_DECLARE_FLAGS(Privileges, Privilege) +Q_DECLARE_OPERATORS_FOR_FLAGS(Privileges) +} + +#endif diff --git a/src/common/etagcache.cpp b/src/common/etagcache.cpp new file mode 100644 index 0000000..f88f218 --- /dev/null +++ b/src/common/etagcache.cpp @@ -0,0 +1,79 @@ +/* + Copyright (c) 2010 Grégory Oestreicher + + 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 "etagcache.h" + +using namespace KDAV; + +EtagCache::EtagCache(QObject *parent) + : QObject(parent) +{ +} + +void EtagCache::setEtag(const QString &remoteId, const QString &etag) +{ + setEtagInternal(remoteId, etag); + + if (mChangedRemoteIds.contains(remoteId)) { + mChangedRemoteIds.remove(remoteId); + } +} + +void EtagCache::setEtagInternal(const QString &remoteId, const QString &etag) +{ + mCache[ remoteId ] = etag; +} + +bool EtagCache::contains(const QString &remoteId) const +{ + return mCache.contains(remoteId); +} + +bool EtagCache::etagChanged(const QString &remoteId, const QString &refEtag) const +{ + if (!contains(remoteId)) { + return true; + } + return mCache.value(remoteId) != refEtag; +} + +void EtagCache::markAsChanged(const QString &remoteId) +{ + mChangedRemoteIds.insert(remoteId); +} + +bool EtagCache::isOutOfDate(const QString &remoteId) const +{ + return mChangedRemoteIds.contains(remoteId); +} + +void EtagCache::removeEtag(const QString &remoteId) +{ + mChangedRemoteIds.remove(remoteId); + mCache.remove(remoteId); +} + +QStringList EtagCache::urls() const +{ + return mCache.keys(); +} + +QStringList EtagCache::changedRemoteIds() const +{ + return mChangedRemoteIds.toList(); +} diff --git a/src/common/etagcache.h b/src/common/etagcache.h new file mode 100644 index 0000000..7e9a26c --- /dev/null +++ b/src/common/etagcache.h @@ -0,0 +1,105 @@ +/* + Copyright (c) 2010 Grégory Oestreicher + + 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 KDAV_ETAGCACHE_H +#define KDAV_ETAGCACHE_H + +#include "kpimkdav_export.h" + +#include +#include +#include +#include + +namespace KDAV { +/** + * @short A helper class to cache etags. + * + * The EtagCache caches the remote ids and etags of all items + * in a given collection. This cache is needed to find + * out which items have been changed in the backend and have to + * be refetched on the next call of ResourceBase::retrieveItems() + */ +class KPIMKDAV_EXPORT EtagCache : public QObject +{ + Q_OBJECT + +public: + /** + * Creates a new etag cache and populates it with the ETags + * of items found in @p collection. + */ + explicit EtagCache(QObject *parent = nullptr); + + /** + * Sets the ETag for the remote ID. If the remote ID is marked as + * changed (is contained in the return of changedRemoteIds), remove + * it from the changed list. + */ + void setEtag(const QString &remoteId, const QString &etag); + + /** + * Checks if the given item is in the cache + */ + Q_REQUIRED_RESULT bool contains(const QString &remoteId) const; + + /** + * Check if the known ETag for the remote ID is equal to @p refEtag. + */ + Q_REQUIRED_RESULT bool etagChanged(const QString &remoteId, const QString &refEtag) const; + + /** + * Mark an item as changed in the backend. + */ + void markAsChanged(const QString &remoteId); + + /** + * Returns true if the remote ID is marked as changed (is contained in the + * return of changedRemoteIds) + */ + Q_REQUIRED_RESULT bool isOutOfDate(const QString &remoteId) const; + + /** + * Removes the entry for item with remote ID @p remoteId. + */ + void removeEtag(const QString &remoteId); + + /** + * Returns the list of all items URLs. + */ + Q_REQUIRED_RESULT QStringList urls() const; + + /** + * Returns the list of remote ids of items that have been changed + * in the backend. + */ + Q_REQUIRED_RESULT QStringList changedRemoteIds() const; + +protected: + /** + * Sets the ETag for the remote ID. + */ + void setEtagInternal(const QString &remoteId, const QString &etag); + +private: + QMap mCache; + QSet mChangedRemoteIds; +}; +} + +#endif diff --git a/src/common/utils.cpp b/src/common/utils.cpp new file mode 100644 index 0000000..5daa284 --- /dev/null +++ b/src/common/utils.cpp @@ -0,0 +1,180 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 "utils.h" + +#include "enums.h" + +#include "davitem.h" +#include "davmanager.h" +#include "davprotocolbase.h" + +#include +#include + +#include "libkdav_debug.h" + +using namespace KDAV; + +QDomElement Utils::firstChildElementNS(const QDomElement &parent, const QString &namespaceUri, const QString &tagName) +{ + for (QDomNode child = parent.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isElement()) { + const QDomElement elt = child.toElement(); + if (tagName.isEmpty() || (elt.tagName() == tagName && elt.namespaceURI() == namespaceUri)) { + return elt; + } + } + } + + return QDomElement(); +} + +QDomElement Utils::nextSiblingElementNS(const QDomElement &element, const QString &namespaceUri, const QString &tagName) +{ + for (QDomNode sib = element.nextSibling(); !sib.isNull(); sib = sib.nextSibling()) { + if (sib.isElement()) { + const QDomElement elt = sib.toElement(); + if (tagName.isEmpty() || (elt.tagName() == tagName && elt.namespaceURI() == namespaceUri)) { + return elt; + } + } + } + + return QDomElement(); +} + +Privileges Utils::extractPrivileges(const QDomElement &element) +{ + Privileges final = None; + QDomElement privElement = firstChildElementNS(element, QStringLiteral("DAV:"), QStringLiteral("privilege")); + + while (!privElement.isNull()) { + QDomElement child = privElement.firstChildElement(); + + while (!child.isNull()) { + final |= parsePrivilege(child); + child = child.nextSiblingElement(); + } + + privElement = Utils::nextSiblingElementNS(privElement, QStringLiteral("DAV:"), QStringLiteral("privilege")); + } + + return final; +} + +Privileges Utils::parsePrivilege(const QDomElement &element) +{ + Privileges final = None; + + if (!element.childNodes().isEmpty()) { + // This is an aggregate privilege, parse each of its children + QDomElement child = element.firstChildElement(); + while (!child.isNull()) { + final |= parsePrivilege(child); + child = child.nextSiblingElement(); + } + } else { + // This is a normal privilege + const QString privname = element.localName(); + + if (privname == QLatin1String("read")) { + final |= KDAV::Read; + } else if (privname == QLatin1String("write")) { + final |= KDAV::Write; + } else if (privname == QLatin1String("write-properties")) { + final |= KDAV::WriteProperties; + } else if (privname == QLatin1String("write-content")) { + final |= KDAV::WriteContent; + } else if (privname == QLatin1String("unlock")) { + final |= KDAV::Unlock; + } else if (privname == QLatin1String("read-acl")) { + final |= KDAV::ReadAcl; + } else if (privname == QLatin1String("read-current-user-privilege-set")) { + final |= KDAV::ReadCurrentUserPrivilegeSet; + } else if (privname == QLatin1String("write-acl")) { + final |= KDAV::WriteAcl; + } else if (privname == QLatin1String("bind")) { + final |= KDAV::Bind; + } else if (privname == QLatin1String("unbind")) { + final |= KDAV::Unbind; + } else if (privname == QLatin1String("all")) { + final |= KDAV::All; + } + } + + return final; +} + +QLatin1String Utils::protocolName(Protocol protocol) +{ + QLatin1String protocolName(""); + + switch (protocol) { + case KDAV::CalDav: + protocolName = QLatin1String("CalDav"); + break; + case KDAV::CardDav: + protocolName = QLatin1String("CardDav"); + break; + case KDAV::GroupDav: + protocolName = QLatin1String("GroupDav"); + break; + } + + return protocolName; +} + +Protocol Utils::protocolByName(const QString &name) +{ + Protocol protocol = KDAV::CalDav; + + if (name == QLatin1String("CalDav")) { + protocol = KDAV::CalDav; + } else if (name == QLatin1String("CardDav")) { + protocol = KDAV::CardDav; + } else if (name == QLatin1String("GroupDav")) { + protocol = KDAV::GroupDav; + } else { + qCCritical(KDAV_LOG) << "Unexpected protocol name : " << name; + } + + return protocol; +} + +QString Utils::createUniqueId() +{ + const qint64 time = QDateTime::currentMSecsSinceEpoch() / 1000; + const int r = qrand() % 1000; + const QString id = QLatin1Char('R') + QString::number(r); + const QString uid = QString::number(time) + QLatin1Char('.') + id; + return uid; +} + +QString Utils::contactsMimeType(Protocol protocol) +{ + QString ret; + + if (protocol == KDAV::CardDav) { + ret = QStringLiteral("text/vcard"); + } else if (protocol == KDAV::GroupDav) { + ret = QStringLiteral("text/x-vcard"); + } + + return ret; +} diff --git a/src/common/utils.h b/src/common/utils.h new file mode 100644 index 0000000..2adf266 --- /dev/null +++ b/src/common/utils.h @@ -0,0 +1,76 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 KDAV_DAVUTILS_H +#define KDAV_DAVUTILS_H + +#include "kpimkdav_export.h" + +#include "enums.h" + +#include + +namespace KDAV { +/** + * @short A namespace that contains helper methods for DAV functionality. + */ +namespace Utils { +/** + * Returns the untranslated name of the given DAV @p protocol dialect. + */ +Q_REQUIRED_RESULT QLatin1String KPIMKDAV_EXPORT protocolName(Protocol protocol); + +/** + * Returns the protocol matching the given name. This is the opposite of + * Utils::protocolName(). + */ +Protocol KPIMKDAV_EXPORT protocolByName(const QString &name); + +/** + * Returns the first child element of @p parent that has the given @p tagName and is part of the @p namespaceUri. + */ +Q_REQUIRED_RESULT QDomElement KPIMKDAV_EXPORT firstChildElementNS(const QDomElement &parent, const QString &namespaceUri, const QString &tagName); + +/** + * Returns the next sibling element of @p element that has the given @p tagName and is part of the @p namespaceUri. + */ +Q_REQUIRED_RESULT QDomElement KPIMKDAV_EXPORT nextSiblingElementNS(const QDomElement &element, const QString &namespaceUri, const QString &tagName); + +/** + * Extracts privileges from @p element. The tags are expected to be first level children of @p element. + */ +Q_REQUIRED_RESULT Privileges KPIMKDAV_EXPORT extractPrivileges(const QDomElement &element); + +/** + * Parses a single tag and returns the final Privileges. + */ +Q_REQUIRED_RESULT Privileges KPIMKDAV_EXPORT parsePrivilege(const QDomElement &element); + +/** + * Creates a unique identifier that can be used as a file name to upload the dav item + */ +Q_REQUIRED_RESULT QString KPIMKDAV_EXPORT createUniqueId(); + +/** + * Returns the mimetype that shall be used for contact DAV resources using @p protocol. + */ +Q_REQUIRED_RESULT QString KPIMKDAV_EXPORT contactsMimeType(Protocol protocol); +} +} + +#endif diff --git a/src/protocols/caldavprotocol.cpp b/src/protocols/caldavprotocol.cpp new file mode 100644 index 0000000..0c0c222 --- /dev/null +++ b/src/protocols/caldavprotocol.cpp @@ -0,0 +1,422 @@ +/* + Copyright (c) 2009 Grégory Oestreicher + + 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 "caldavprotocol.h" +#include "common/utils.h" + +#include +#include +#include + +using namespace KDAV; + +class CaldavCollectionQueryBuilder : public XMLQueryBuilder +{ +public: + QDomDocument buildQuery() const override + { + QDomDocument document; + + QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); + document.appendChild(propfindElement); + + QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + propfindElement.appendChild(propElement); + + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname"))); + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"))); + propElement.appendChild(document.createElementNS(QStringLiteral("http://apple.com/ns/ical/"), QStringLiteral("calendar-color"))); + propElement.appendChild(document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("supported-calendar-component-set"))); + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("current-user-privilege-set"))); + propElement.appendChild(document.createElementNS(QStringLiteral("http://calendarserver.org/ns/"), QStringLiteral("getctag"))); + + return document; + } + + QString mimeType() const override + { + return QString(); + } +}; + +class CaldavListEventQueryBuilder : public XMLQueryBuilder +{ +public: + QDomDocument buildQuery() const override + { + QString startTime = parameter(QStringLiteral("start")).toString(); + QString endTime = parameter(QStringLiteral("end")).toString(); + QDomDocument document; + + QDomElement queryElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-query")); + document.appendChild(queryElement); + + QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + queryElement.appendChild(propElement); + + QDomElement getetagElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag")); + propElement.appendChild(getetagElement); + + QDomElement getRTypeElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype")); + propElement.appendChild(getRTypeElement); + + QDomElement filterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("filter")); + queryElement.appendChild(filterElement); + + QDomElement compfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); + + QDomAttr nameAttribute = document.createAttribute(QStringLiteral("name")); + nameAttribute.setValue(QStringLiteral("VCALENDAR")); + compfilterElement.setAttributeNode(nameAttribute); + filterElement.appendChild(compfilterElement); + + QDomElement subcompfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); + nameAttribute = document.createAttribute(QStringLiteral("name")); + nameAttribute.setValue(QStringLiteral("VEVENT")); + subcompfilterElement.setAttributeNode(nameAttribute); + + if (!startTime.isEmpty() || !endTime.isEmpty()) { + QDomElement timeRangeElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("time-range")); + + if (!startTime.isEmpty()) { + QDomAttr startAttribute = document.createAttribute(QStringLiteral("start")); + startAttribute.setValue(startTime); + timeRangeElement.setAttributeNode(startAttribute); + } + + if (!endTime.isEmpty()) { + QDomAttr endAttribute = document.createAttribute(QStringLiteral("end")); + endAttribute.setValue(endTime); + timeRangeElement.setAttributeNode(endAttribute); + } + + subcompfilterElement.appendChild(timeRangeElement); + } + + compfilterElement.appendChild(subcompfilterElement); + + return document; + } + + QString mimeType() const override + { + return QStringLiteral("application/x-vnd.akonadi.calendar.event"); + } +}; + +class CaldavListTodoQueryBuilder : public XMLQueryBuilder +{ +public: + QDomDocument buildQuery() const override + { + QString startTime = parameter(QStringLiteral("start")).toString(); + QString endTime = parameter(QStringLiteral("end")).toString(); + QDomDocument document; + + QDomElement queryElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-query")); + document.appendChild(queryElement); + + QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + queryElement.appendChild(propElement); + + QDomElement getetagElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag")); + propElement.appendChild(getetagElement); + + QDomElement getRTypeElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype")); + propElement.appendChild(getRTypeElement); + + QDomElement filterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("filter")); + queryElement.appendChild(filterElement); + + QDomElement compfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); + + QDomAttr nameAttribute = document.createAttribute(QStringLiteral("name")); + nameAttribute.setValue(QStringLiteral("VCALENDAR")); + compfilterElement.setAttributeNode(nameAttribute); + filterElement.appendChild(compfilterElement); + + QDomElement subcompfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); + nameAttribute = document.createAttribute(QStringLiteral("name")); + nameAttribute.setValue(QStringLiteral("VTODO")); + subcompfilterElement.setAttributeNode(nameAttribute); + + if (!startTime.isEmpty() || !endTime.isEmpty()) { + QDomElement timeRangeElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("time-range")); + + if (!startTime.isEmpty()) { + QDomAttr startAttribute = document.createAttribute(QStringLiteral("start")); + startAttribute.setValue(startTime); + timeRangeElement.setAttributeNode(startAttribute); + } + + if (!endTime.isEmpty()) { + QDomAttr endAttribute = document.createAttribute(QStringLiteral("end")); + endAttribute.setValue(endTime); + timeRangeElement.setAttributeNode(endAttribute); + } + + subcompfilterElement.appendChild(timeRangeElement); + } + + compfilterElement.appendChild(subcompfilterElement); + + return document; + } + + QString mimeType() const override + { + return QStringLiteral("application/x-vnd.akonadi.calendar.todo"); + } +}; + +class CaldavListJournalQueryBuilder : public XMLQueryBuilder +{ +public: + QDomDocument buildQuery() const override + { + QString startTime = parameter(QStringLiteral("start")).toString(); + QString endTime = parameter(QStringLiteral("end")).toString(); + QDomDocument document; + + QDomElement queryElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-query")); + document.appendChild(queryElement); + + QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + queryElement.appendChild(propElement); + + QDomElement getetagElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag")); + propElement.appendChild(getetagElement); + + QDomElement getRTypeElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype")); + propElement.appendChild(getRTypeElement); + + QDomElement filterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("filter")); + queryElement.appendChild(filterElement); + + QDomElement compfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); + + QDomAttr nameAttribute = document.createAttribute(QStringLiteral("name")); + nameAttribute.setValue(QStringLiteral("VCALENDAR")); + compfilterElement.setAttributeNode(nameAttribute); + filterElement.appendChild(compfilterElement); + + QDomElement subcompfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); + nameAttribute = document.createAttribute(QStringLiteral("name")); + nameAttribute.setValue(QStringLiteral("VJOURNAL")); + subcompfilterElement.setAttributeNode(nameAttribute); + + if (!startTime.isEmpty() || !endTime.isEmpty()) { + QDomElement timeRangeElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("time-range")); + + if (!startTime.isEmpty()) { + QDomAttr startAttribute = document.createAttribute(QStringLiteral("start")); + startAttribute.setValue(startTime); + timeRangeElement.setAttributeNode(startAttribute); + } + + if (!endTime.isEmpty()) { + QDomAttr endAttribute = document.createAttribute(QStringLiteral("end")); + endAttribute.setValue(endTime); + timeRangeElement.setAttributeNode(endAttribute); + } + + subcompfilterElement.appendChild(timeRangeElement); + } + + compfilterElement.appendChild(subcompfilterElement); + + return document; + } + + QString mimeType() const override + { + return QStringLiteral("application/x-vnd.akonadi.calendar.journal"); + } +}; + +class CaldavMultigetQueryBuilder : public XMLQueryBuilder +{ +public: + QDomDocument buildQuery() const override + { + QDomDocument document; + const QStringList urls = parameter(QStringLiteral("urls")).toStringList(); + if (urls.isEmpty()) { + return document; + } + + QDomElement multigetElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-multiget")); + document.appendChild(multigetElement); + + QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + multigetElement.appendChild(propElement); + + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag"))); + propElement.appendChild(document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-data"))); + + for (const QString &url : urls) { + QDomElement hrefElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("href")); + const QUrl pathUrl = QUrl::fromUserInput(url); + const QDomText textNode = document.createTextNode(pathUrl.toString()); + hrefElement.appendChild(textNode); + + multigetElement.appendChild(hrefElement); + } + + return document; + } + + QString mimeType() const override + { + return QString(); + } +}; + +CaldavProtocol::CaldavProtocol() +{ +} + +bool CaldavProtocol::supportsPrincipals() const +{ + return true; +} + +bool CaldavProtocol::useReport() const +{ + return true; +} + +bool CaldavProtocol::useMultiget() const +{ + return true; +} + +QString CaldavProtocol::principalHomeSet() const +{ + return QStringLiteral("calendar-home-set"); +} + +QString CaldavProtocol::principalHomeSetNS() const +{ + return QStringLiteral("urn:ietf:params:xml:ns:caldav"); +} + +XMLQueryBuilder::Ptr CaldavProtocol::collectionsQuery() const +{ + return XMLQueryBuilder::Ptr(new CaldavCollectionQueryBuilder()); +} + +QString CaldavProtocol::collectionsXQuery() const +{ + //const QString query( "//*[local-name()='calendar' and namespace-uri()='urn:ietf:params:xml:ns:caldav']/ancestor::*[local-name()='prop' and namespace-uri()='DAV:']/*[local-name()='supported-calendar-component-set' and namespace-uri()='urn:ietf:params:xml:ns:caldav']/*[local-name()='comp' and namespace-uri()='urn:ietf:params:xml:ns:caldav' and (@name='VTODO' or @name='VEVENT')]/ancestor::*[local-name()='response' and namespace-uri()='DAV:']" ); + const QString query(QStringLiteral( + "//*[local-name()='calendar' and namespace-uri()='urn:ietf:params:xml:ns:caldav']/ancestor::*[local-name()='prop' and namespace-uri()='DAV:']/ancestor::*[local-name()='response' and namespace-uri()='DAV:']")); + + return query; +} + +QVector CaldavProtocol::itemsQueries() const +{ + QVector ret; + ret << XMLQueryBuilder::Ptr(new CaldavListEventQueryBuilder()); + ret << XMLQueryBuilder::Ptr(new CaldavListTodoQueryBuilder()); + ret << XMLQueryBuilder::Ptr(new CaldavListJournalQueryBuilder()); + return ret; +} + +XMLQueryBuilder::Ptr CaldavProtocol::itemsReportQuery(const QStringList &urls) const +{ + XMLQueryBuilder::Ptr ret(new CaldavMultigetQueryBuilder()); + ret->setParameter(QStringLiteral("urls"), urls); + return ret; +} + +QString CaldavProtocol::responseNamespace() const +{ + return QStringLiteral("urn:ietf:params:xml:ns:caldav"); +} + +QString CaldavProtocol::dataTagName() const +{ + return QStringLiteral("calendar-data"); +} + +DavCollection::ContentTypes CaldavProtocol::collectionContentTypes(const QDomElement &propstatElement) const +{ + /* + * Extract the content type information from a propstat like the following + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * Test1 User + * + * HTTP/1.1 200 OK + * + */ + + const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); + const QDomElement supportedcomponentElement = Utils::firstChildElementNS(propElement, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("supported-calendar-component-set")); + + DavCollection::ContentTypes contentTypes; + QDomElement compElement = Utils::firstChildElementNS(supportedcomponentElement, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp")); + + /* + * Assign the content-type if the server didn't return anything. + * According to RFC4791, §5.2.3: + * In the absence of this property, the server MUST accept all + * component types, and the client can assume that all component + * types are accepted. + */ + if (compElement.isNull()) { + contentTypes |= DavCollection::Calendar; + contentTypes |= DavCollection::Events; + contentTypes |= DavCollection::Todos; + contentTypes |= DavCollection::FreeBusy; + contentTypes |= DavCollection::Journal; + } + + while (!compElement.isNull()) { + const QString type = compElement.attribute(QStringLiteral("name")).toLower(); + if (type == QLatin1String("vcalendar")) { + contentTypes |= DavCollection::Calendar; + } else if (type == QLatin1String("vevent")) { + contentTypes |= DavCollection::Events; + } else if (type == QLatin1String("vtodo")) { + contentTypes |= DavCollection::Todos; + } else if (type == QLatin1String("vfreebusy")) { + contentTypes |= DavCollection::FreeBusy; + } else if (type == QLatin1String("vjournal")) { + contentTypes |= DavCollection::Journal; + } + + compElement = Utils::nextSiblingElementNS(compElement, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp")); + } + + return contentTypes; +} diff --git a/src/protocols/caldavprotocol.h b/src/protocols/caldavprotocol.h new file mode 100644 index 0000000..7fda826 --- /dev/null +++ b/src/protocols/caldavprotocol.h @@ -0,0 +1,43 @@ +/* + Copyright (c) 2009 Grégory Oestreicher + + 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 KDAV_CALDAVPROTOCOL_H +#define KDAV_CALDAVPROTOCOL_H + +#include "common/davmultigetprotocol.h" + +class CaldavProtocol : public KDAV::DavMultigetProtocol +{ +public: + CaldavProtocol(); + Q_REQUIRED_RESULT bool supportsPrincipals() const override; + Q_REQUIRED_RESULT bool useReport() const override; + Q_REQUIRED_RESULT bool useMultiget() const override; + Q_REQUIRED_RESULT QString principalHomeSet() const override; + Q_REQUIRED_RESULT QString principalHomeSetNS() const override; + Q_REQUIRED_RESULT KDAV::XMLQueryBuilder::Ptr collectionsQuery() const override; + Q_REQUIRED_RESULT QString collectionsXQuery() const override; + Q_REQUIRED_RESULT QVector itemsQueries() const override; + Q_REQUIRED_RESULT KDAV::XMLQueryBuilder::Ptr itemsReportQuery(const QStringList &urls) const override; + Q_REQUIRED_RESULT QString responseNamespace() const override; + Q_REQUIRED_RESULT QString dataTagName() const override; + + Q_REQUIRED_RESULT KDAV::DavCollection::ContentTypes collectionContentTypes(const QDomElement &propstat) const override; +}; + +#endif diff --git a/src/protocols/carddavprotocol.cpp b/src/protocols/carddavprotocol.cpp new file mode 100644 index 0000000..9449ace --- /dev/null +++ b/src/protocols/carddavprotocol.cpp @@ -0,0 +1,187 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 "carddavprotocol.h" + +#include +#include +#include + +using namespace KDAV; + +class CarddavCollectionQueryBuilder : public XMLQueryBuilder +{ +public: + QDomDocument buildQuery() const override + { + QDomDocument document; + + QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); + document.appendChild(propfindElement); + + QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + propfindElement.appendChild(propElement); + + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname"))); + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"))); + propElement.appendChild(document.createElementNS(QStringLiteral("http://calendarserver.org/ns/"), QStringLiteral("getctag"))); + + return document; + } + + QString mimeType() const override + { + return QString(); + } +}; + +class CarddavListItemsQueryBuilder : public XMLQueryBuilder +{ +public: + QDomDocument buildQuery() const override + { + QDomDocument document; + + QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); + document.appendChild(propfindElement); + + QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + propfindElement.appendChild(propElement); + + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname"))); + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"))); + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag"))); + + return document; + } + + QString mimeType() const override + { + return QStringLiteral("text/directory"); + } +}; + +class CarddavMultigetQueryBuilder : public XMLQueryBuilder +{ +public: + QDomDocument buildQuery() const override + { + QDomDocument document; + const QStringList urls = parameter(QStringLiteral("urls")).toStringList(); + if (urls.isEmpty()) { + return document; + } + + QDomElement multigetElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:carddav"), QStringLiteral("addressbook-multiget")); + document.appendChild(multigetElement); + + QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + multigetElement.appendChild(propElement); + + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag"))); + QDomElement addressDataElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:carddav"), QStringLiteral("address-data")); + addressDataElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("allprop"))); + propElement.appendChild(addressDataElement); + + for (const QString &url : urls) { + QDomElement hrefElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("href")); + const QUrl pathUrl = QUrl::fromUserInput(url); + const QDomText textNode = document.createTextNode(pathUrl.toString()); + hrefElement.appendChild(textNode); + + multigetElement.appendChild(hrefElement); + } + + return document; + } + + QString mimeType() const override + { + return QString(); + } +}; + +CarddavProtocol::CarddavProtocol() +{ +} + +bool CarddavProtocol::supportsPrincipals() const +{ + return true; +} + +bool CarddavProtocol::useReport() const +{ + return false; +} + +bool CarddavProtocol::useMultiget() const +{ + return true; +} + +QString CarddavProtocol::principalHomeSet() const +{ + return QStringLiteral("addressbook-home-set"); +} + +QString CarddavProtocol::principalHomeSetNS() const +{ + return QStringLiteral("urn:ietf:params:xml:ns:carddav"); +} + +XMLQueryBuilder::Ptr CarddavProtocol::collectionsQuery() const +{ + return XMLQueryBuilder::Ptr(new CarddavCollectionQueryBuilder()); +} + +QString CarddavProtocol::collectionsXQuery() const +{ + const QString query(QStringLiteral("//*[local-name()='addressbook' and namespace-uri()='urn:ietf:params:xml:ns:carddav']/ancestor::*[local-name()='response' and namespace-uri()='DAV:']")); + + return query; +} + +QVector CarddavProtocol::itemsQueries() const +{ + QVector ret; + ret << XMLQueryBuilder::Ptr(new CarddavListItemsQueryBuilder()); + return ret; +} + +XMLQueryBuilder::Ptr CarddavProtocol::itemsReportQuery(const QStringList &urls) const +{ + XMLQueryBuilder::Ptr ret(new CarddavMultigetQueryBuilder()); + ret->setParameter(QStringLiteral("urls"), urls); + return ret; +} + +QString CarddavProtocol::responseNamespace() const +{ + return QStringLiteral("urn:ietf:params:xml:ns:carddav"); +} + +QString CarddavProtocol::dataTagName() const +{ + return QStringLiteral("address-data"); +} + +DavCollection::ContentTypes CarddavProtocol::collectionContentTypes(const QDomElement &) const +{ + return DavCollection::Contacts; +} diff --git a/src/protocols/carddavprotocol.h b/src/protocols/carddavprotocol.h new file mode 100644 index 0000000..1eaf98a --- /dev/null +++ b/src/protocols/carddavprotocol.h @@ -0,0 +1,43 @@ +/* + Copyright (c) 2010 Tobias Koenig + + 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 KDAV_CARDDAVPROTOCOL_H +#define KDAV_CARDDAVPROTOCOL_H + +#include "common/davmultigetprotocol.h" + +class CarddavProtocol : public KDAV::DavMultigetProtocol +{ +public: + CarddavProtocol(); + Q_REQUIRED_RESULT bool supportsPrincipals() const override; + Q_REQUIRED_RESULT bool useReport() const override; + Q_REQUIRED_RESULT bool useMultiget() const override; + Q_REQUIRED_RESULT QString principalHomeSet() const override; + Q_REQUIRED_RESULT QString principalHomeSetNS() const override; + Q_REQUIRED_RESULT KDAV::XMLQueryBuilder::Ptr collectionsQuery() const override; + Q_REQUIRED_RESULT QString collectionsXQuery() const override; + Q_REQUIRED_RESULT QVector itemsQueries() const override; + Q_REQUIRED_RESULT KDAV::XMLQueryBuilder::Ptr itemsReportQuery(const QStringList &urls) const override; + Q_REQUIRED_RESULT QString responseNamespace() const override; + Q_REQUIRED_RESULT QString dataTagName() const override; + + Q_REQUIRED_RESULT KDAV::DavCollection::ContentTypes collectionContentTypes(const QDomElement &propstat) const override; +}; + +#endif diff --git a/src/protocols/groupdavprotocol.cpp b/src/protocols/groupdavprotocol.cpp new file mode 100644 index 0000000..5294398 --- /dev/null +++ b/src/protocols/groupdavprotocol.cpp @@ -0,0 +1,153 @@ +/* + Copyright (c) 2009 Grégory Oestreicher + + 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 "groupdavprotocol.h" + +#include "common/utils.h" + +#include + +using namespace KDAV; + +class GroupdavCollectionQueryBuilder : public XMLQueryBuilder +{ +public: + QDomDocument buildQuery() const override + { + QDomDocument document; + + QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); + document.appendChild(propfindElement); + + QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + propfindElement.appendChild(propElement); + + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname"))); + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"))); + + return document; + } + + QString mimeType() const override + { + return QString(); + } +}; + +class GroupdavItemQueryBuilder : public XMLQueryBuilder +{ +public: + QDomDocument buildQuery() const override + { + QDomDocument document; + + QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); + document.appendChild(propfindElement); + + QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); + propfindElement.appendChild(propElement); + + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname"))); + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"))); + propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag"))); + + return document; + } + + QString mimeType() const override + { + return QString(); + } +}; + +GroupdavProtocol::GroupdavProtocol() +{ +} + +bool GroupdavProtocol::supportsPrincipals() const +{ + return false; +} + +bool GroupdavProtocol::useReport() const +{ + return false; +} + +bool GroupdavProtocol::useMultiget() const +{ + return false; +} + +XMLQueryBuilder::Ptr GroupdavProtocol::collectionsQuery() const +{ + return XMLQueryBuilder::Ptr(new GroupdavCollectionQueryBuilder()); +} + +QString GroupdavProtocol::collectionsXQuery() const +{ + const QString query(QStringLiteral( + "//*[(local-name()='vevent-collection' or local-name()='vtodo-collection' or local-name()='vcard-collection') and namespace-uri()='http://groupdav.org/']/ancestor::*[local-name()='response' and namespace-uri()='DAV:']")); + + return query; +} + +QVector GroupdavProtocol::itemsQueries() const +{ + QVector ret; + ret << XMLQueryBuilder::Ptr(new GroupdavItemQueryBuilder()); + return ret; +} + +DavCollection::ContentTypes GroupdavProtocol::collectionContentTypes(const QDomElement &propstatElement) const +{ + /* + * Extract the content type information from a propstat like the following + * + * + * HTTP/1.1 200 OK + * + * Tasks + * + * + * + * + * Sat, 30 Jan 2010 17:52:41 -0100 + * + * + */ + + const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); + const QDomElement resourcetypeElement = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("resourcetype")); + + DavCollection::ContentTypes contentTypes; + + if (!Utils::firstChildElementNS(resourcetypeElement, QStringLiteral("http://groupdav.org/"), QStringLiteral("vevent-collection")).isNull()) { + contentTypes |= DavCollection::Events; + } + + if (!Utils::firstChildElementNS(resourcetypeElement, QStringLiteral("http://groupdav.org/"), QStringLiteral("vtodo-collection")).isNull()) { + contentTypes |= DavCollection::Todos; + } + + if (!Utils::firstChildElementNS(resourcetypeElement, QStringLiteral("http://groupdav.org/"), QStringLiteral("vcard-collection")).isNull()) { + contentTypes |= DavCollection::Contacts; + } + + return contentTypes; +} diff --git a/src/protocols/groupdavprotocol.h b/src/protocols/groupdavprotocol.h new file mode 100644 index 0000000..e89ad9f --- /dev/null +++ b/src/protocols/groupdavprotocol.h @@ -0,0 +1,38 @@ +/* + Copyright (c) 2009 Grégory Oestreicher + + 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 GROUPDAVPROTOCOL_H +#define GROUPDAVPROTOCOL_H + +#include "common/davprotocolbase.h" + +class GroupdavProtocol : public KDAV::DavProtocolBase +{ +public: + GroupdavProtocol(); + Q_REQUIRED_RESULT bool supportsPrincipals() const override; + Q_REQUIRED_RESULT bool useReport() const override; + Q_REQUIRED_RESULT bool useMultiget() const override; + Q_REQUIRED_RESULT KDAV::XMLQueryBuilder::Ptr collectionsQuery() const override; + Q_REQUIRED_RESULT QString collectionsXQuery() const override; + Q_REQUIRED_RESULT QVector itemsQueries() const override; + + Q_REQUIRED_RESULT KDAV::DavCollection::ContentTypes collectionContentTypes(const QDomElement &propstat) const override; +}; + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..d91c4f7 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,15 @@ +kde_enable_exceptions() +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) + +set(testserver_SRCS + testserver.cpp +) + +add_executable(testserver ${testserver_SRCS}) + +target_link_libraries(testserver + Qt5::Core + KPim::KDAV + Qt5::Xml + ) + diff --git a/test/testserver.cpp b/test/testserver.cpp new file mode 100644 index 0000000..8043665 --- /dev/null +++ b/test/testserver.cpp @@ -0,0 +1,163 @@ +/* + Copyright (c) 2016 Sandro Knauß + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + QUrl mainUrl(QStringLiteral("https://apps.kolabnow.com/addressbooks/test1%40kolab.org")); + mainUrl.setUserName(QStringLiteral("test1@kolab.org")); + mainUrl.setPassword(QStringLiteral("Welcome2KolabSystems")); + KDAV::DavUrl davUrl(mainUrl, KDAV::CardDav); + + auto *job = new KDAV::DavCollectionsFetchJob(davUrl); + job->exec(); + + foreach(const auto collection, job->collections()) { + qDebug() << collection.displayName() << "PRIVS: " << collection.privileges(); + auto collectionUrl = collection.url(); + std::shared_ptr cache(new KDAV::EtagCache()); + int anz = -1; + //Get all items in a collection add them to cache and make sure, that afterward no item is changed + { + auto itemListJob = new KDAV::DavItemsListJob(collectionUrl, cache); + itemListJob->exec(); + anz = itemListJob->items().size(); + qDebug() << "items:" << itemListJob->items().size(); + qDebug() << "changed Items:" << itemListJob->changedItems().size(); + qDebug() << "deleted Items:" << itemListJob->deletedItems(); + foreach(const auto item, itemListJob->changedItems()) { + qDebug() << item.url().url() << item.contentType() << item.data(); + auto itemFetchJob = new KDAV::DavItemFetchJob(item); + itemFetchJob->exec(); + const auto fetchedItem = itemFetchJob->item(); + qDebug() << fetchedItem.contentType() << fetchedItem.data(); + + auto itemsFetchJob = new KDAV::DavItemsFetchJob(collectionUrl, QStringList() << item.url().toDisplayString()); + itemsFetchJob->exec(); + if (itemsFetchJob->item(item.url().toDisplayString()).contentType() != fetchedItem.contentType()) { //itemsfetchjob do not get contentType + qDebug() << "Fetched same item but got different contentType:" << itemsFetchJob->item(item.url().toDisplayString()).contentType(); + } + + if (itemsFetchJob->item(item.url().toDisplayString()).data() != fetchedItem.data()) { + qDebug() << "Fetched same item but got different data:" << itemsFetchJob->item(item.url().toDisplayString()).data(); + } + + cache->setEtag(item.url().toDisplayString(), item.etag()); + } + cache->setEtag(QStringLiteral("invalid"),QStringLiteral("invalid")); + } + { + qDebug() << "second run: (should be empty)."; + auto itemListJob = new KDAV::DavItemsListJob(collectionUrl, cache); + itemListJob->exec(); + if (itemListJob->items().size() != anz) { + qDebug() << "Items have added/deleted on server."; + } + if (itemListJob->changedItems().size() != 0) { + qDebug() << "Items have changed on server."; + } + if (itemListJob->deletedItems() != QStringList() << QStringLiteral("invalid")) { + qDebug() << "more items deleted:" << itemListJob->deletedItems(); + } + } + } + + { + QUrl url(QStringLiteral("https://apps.kolabnow.com/addressbooks/test1%40kolab.org/cbbf386d-7e9b-4e72-947d-0b813ea9b347/")); + url.setUserInfo(mainUrl.userInfo()); + KDAV::DavUrl collectionUrl(url, KDAV::CardDav); + auto collectionDeleteJob = new KDAV::DavCollectionDeleteJob(collectionUrl); + collectionDeleteJob->exec(); + if (collectionDeleteJob->error()) { + qDebug() << collectionDeleteJob->errorString(); + } + } + + { + QUrl url(QStringLiteral("https://apps.kolabnow.com/addressbooks/test1%40kolab.org/9290e784-c876-412f-8385-be292d64b2c6/")); + url.setUserInfo(mainUrl.userInfo()); + KDAV::DavUrl testCollectionUrl(url, KDAV::CardDav); + auto collectionModifyJob = new KDAV::DavCollectionModifyJob(testCollectionUrl); + collectionModifyJob->setProperty(QStringLiteral("displayname"), QStringLiteral("test234")); + collectionModifyJob->exec(); + if (collectionModifyJob->error()) { + qDebug() << collectionModifyJob->errorString(); + } + } + + //create element with "wrong put url" test if we get the correct url back + { + QUrl url(QStringLiteral("https://apps.kolabnow.com/addressbooks/test1%40kolab.org/9290e784-c876-412f-8385-be292d64b2c6/xxx.vcf")); + url.setUserInfo(mainUrl.userInfo()); + KDAV::DavUrl testItemUrl(url, KDAV::CardDav); + QByteArray data = "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Kolab//iRony DAV Server 0.3.1//Sabre//Sabre VObject 2.1.7//EN\r\nUID:12345678-1234-1234-1234-123456789abc\r\nFN:John Doe\r\nN:Doe;John;;;\r\nEMAIL;TYPE=INTERNET;TYPE=HOME:john.doe@example.com\r\nREV;VALUE=DATE-TIME:20161221T145611Z\r\nEND:VCARD\r\n"; + KDAV::DavItem item(testItemUrl, QStringLiteral("text/x-vcard"), data, QString()); + auto createJob = new KDAV::DavItemCreateJob(item); + createJob->exec(); + if (createJob->error()) { + qDebug() << createJob->errorString(); + } + if (createJob->item().url().toDisplayString() != QStringLiteral("https://apps.kolabnow.com/addressbooks/test1%40kolab.org/9290e784-c876-412f-8385-be292d64b2c6/12345678-1234-1234-1234-123456789abc.vcf")) { + qDebug() << "unexpected url" << createJob->item().url().url(); + } + } + + { + QUrl url(QStringLiteral("https://apps.kolabnow.com/addressbooks/test1%40kolab.org/9290e784-c876-412f-8385-be292d64b2c6/12345678-1234-1234-1234-123456789abc.vcf")); + url.setUserInfo(mainUrl.userInfo()); + KDAV::DavUrl testItemUrl(url, KDAV::CardDav); + QByteArray data = "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Kolab//iRony DAV Server 0.3.1//Sabre//Sabre VObject 2.1.7//EN\r\nUID:12345678-1234-1234-1234-123456789abc\r\nFN:John2 Doe\r\nN:Doe;John2;;;\r\nEMAIL;TYPE=INTERNET;TYPE=HOME:john2.doe@example.com\r\nREV;VALUE=DATE-TIME:20161221T145611Z\r\nEND:VCARD\r\n"; + KDAV::DavItem item(testItemUrl, QStringLiteral("text/x-vcard"), data, QString()); + auto modifyJob = new KDAV::DavItemModifyJob(item); + modifyJob->exec(); + if (modifyJob->error()) { + qDebug() << modifyJob->errorString(); + } + } + + { + QUrl url(QStringLiteral("https://apps.kolabnow.com/addressbooks/test1%40kolab.org/9290e784-c876-412f-8385-be292d64b2c6/12345678-1234-1234-1234-123456789abc.vcf")); + url.setUserInfo(mainUrl.userInfo()); + KDAV::DavUrl testItemUrl(url, KDAV::CardDav); + QByteArray data = "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Kolab//iRony DAV Server 0.3.1//Sabre//Sabre VObject 2.1.7//EN\r\nUID:12345678-1234-1234-1234-123456789abc\r\nFN:John2 Doe\r\nN:Doe;John2;;;\r\nEMAIL;TYPE=INTERNET;TYPE=HOME:john2.doe@example.com\r\nREV;VALUE=DATE-TIME:20161221T145611Z\r\nEND:VCARD\r\n"; + KDAV::DavItem item(testItemUrl, QStringLiteral("text/x-vcard"), data, QString()); + auto deleteJob = new KDAV::DavItemDeleteJob(item); + deleteJob->exec(); + if (deleteJob->error()) { + qDebug() << deleteJob->errorString(); + } + } +}