diff --git a/LICENSES/GPL-2.0-or-later.txt b/LICENSES/GPL-2.0-or-later.txt new file mode 100644 index 0000000..1d80ac3 --- /dev/null +++ b/LICENSES/GPL-2.0-or-later.txt @@ -0,0 +1,319 @@ +GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. + +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. This General Public License applies to +most of the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software +is covered by the GNU Lesser 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. + +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., 51 Franklin +Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when +it starts in an interactive mode: + +Gnomovision version 69, Copyright (C) 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 Lesser General Public License instead +of this License. diff --git a/LICENSES/LGPL-2.0-or-later.txt b/LICENSES/LGPL-2.0-or-later.txt new file mode 100644 index 0000000..5c96471 --- /dev/null +++ b/LICENSES/LGPL-2.0-or-later.txt @@ -0,0 +1,446 @@ +GNU LIBRARY GENERAL PUBLIC LICENSE + +Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. + +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is numbered 2 because +it goes with version 2 of the ordinary GPL.] + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public Licenses are intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. + +This license, the Library General Public License, applies to some specially +designated Free Software Foundation software, and to any other libraries whose +authors decide to use it. You can use it for your libraries, 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 library, or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for +a fee, you must give the recipients all the rights that we gave you. You must +make sure that they, too, receive or can get the source code. If you link +a program with the library, you must provide complete object files to the +recipients so that they can relink them with the library, after making changes +to the library and recompiling it. And you must show them these terms so they +know their rights. + +Our method of protecting your rights has two steps: (1) copyright the library, +and (2) offer you this license which gives you legal permission to copy, distribute +and/or modify the library. + +Also, for each distributor's protection, we want to make certain that everyone +understands that there is no warranty for this free library. If the library +is modified by someone else and passed on, we want its recipients to know +that what they have is not the original version, 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 companies distributing free software will individually +obtain patent licenses, thus in effect transforming the program into proprietary +software. To prevent this, we have made it clear that any patent must be licensed +for everyone's free use or not licensed at all. + +Most GNU software, including some libraries, is covered by the ordinary GNU +General Public License, which was designed for utility programs. This license, +the GNU Library General Public License, applies to certain designated libraries. +This license is quite different from the ordinary one; be sure to read it +in full, and don't assume that anything in it is the same as in the ordinary +license. + +The reason we have a separate public license for some libraries is that they +blur the distinction we usually make between modifying or adding to a program +and simply using it. Linking a program with a library, without changing the +library, is in some sense simply using the library, and is analogous to running +a utility program or application program. However, in a textual and legal +sense, the linked executable is a combined work, a derivative of the original +library, and the ordinary General Public License treats it as such. + +Because of this blurred distinction, using the ordinary General Public License +for libraries did not effectively promote software sharing, because most developers +did not use the libraries. We concluded that weaker conditions might promote +sharing better. + +However, unrestricted linking of non-free programs would deprive the users +of those programs of all benefit from the free status of the libraries themselves. +This Library General Public License is intended to permit developers of non-free +programs to use free libraries, while preserving your freedom as a user of +such programs to change the free libraries that are incorporated in them. +(We have not seen how to achieve this as regards changes in header files, +but we have achieved it as regards changes in the actual functions of the +Library.) The hope is that this will lead to faster development of free libraries. + +The precise terms and conditions for copying, distribution and modification +follow. Pay close attention to the difference between a "work based on the +library" and a "work that uses the library". The former contains code derived +from the library, while the latter only works together with the library. + +Note that it is possible for a library to be covered by the ordinary General +Public License rather than by this special one. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library which contains a +notice placed by the copyright holder or other authorized party saying it +may be distributed under the terms of this Library General Public License +(also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared +so as to be conveniently linked with application programs (which use some +of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has +been distributed under these terms. A "work based on the Library" means either +the Library or any derivative work under copyright law: that is to say, a +work containing the Library or a portion of it, either verbatim or with modifications +and/or translated straightforwardly into another language. (Hereinafter, translation +is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications +to it. For a library, complete source code means all the source code for all +modules it contains, plus any associated interface definition files, plus +the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered +by this License; they are outside its scope. The act of running a program +using the Library is not restricted, and output from such a program is covered +only if its contents constitute a work based on the Library (independent of +the use of the Library in a tool for writing it). Whether that is true depends +on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and disclaimer +of warranty; keep intact all the notices that refer to this License and to +the absence of any warranty; and distribute a copy of this License along with +the Library. + +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, +thus forming a work based on the Library, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all +of these conditions: + + a) The modified work must itself be a software library. + +b) You must cause the files modified to carry prominent notices stating that +you changed the files and the date of any change. + +c) You must cause the whole of the work to be licensed at no charge to all +third parties under the terms of this License. + +d) If a facility in the modified Library refers to a function or a table of +data to be supplied by an application program that uses the facility, other +than as an argument passed when the facility is invoked, then you must make +a good faith effort to ensure that, in the event an application does not supply +such function or table, the facility still operates, and performs whatever +part of its purpose remains meaningful. + +(For example, a function in a library to compute square roots has a purpose +that is entirely well-defined independent of the application. Therefore, Subsection +2d requires that any application-supplied function or table used by this function +must be optional: if the application does not supply it, the square root function +must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Library, and can be reasonably +considered independent and separate works in themselves, then this License, +and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole +which is a work based on the Library, the distribution of the whole must be +on the terms of this License, whose permissions for other licensees extend +to the entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise +the right to control the distribution of derivative or collective works based +on the Library. + +In addition, mere aggregation of another work not based on the Library with +the Library (or with a work based on the Library) on a volume of a storage +or distribution medium does not bring the other work under the scope of this +License. + +3. You may opt to apply the terms of the ordinary GNU General Public License +instead of this License to a given copy of the Library. To do this, you must +alter all the notices that refer to this License, so that they refer to the +ordinary GNU General Public License, version 2, instead of to this License. +(If a newer version than version 2 of the ordinary GNU General Public License +has appeared, then you can specify that version instead if you wish.) Do not +make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, +so the ordinary GNU General Public License applies to all subsequent copies +and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library +into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of +it, under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you accompany it with the complete corresponding +machine-readable source code, which must be distributed under the terms of +Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated +place, then offering equivalent access to copy the source code from the same +place satisfies the requirement to distribute the source code, even though +third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but +is designed to work with the Library by being compiled or linked with it, +is called a "work that uses the Library". Such a work, in isolation, is not +a derivative work of the Library, and therefore falls outside the scope of +this License. + +However, linking a "work that uses the Library" with the Library creates an +executable that is a derivative of the Library (because it contains portions +of the Library), rather than a "work that uses the library". The executable +is therefore covered by this License. Section 6 states terms for distribution +of such executables. + +When a "work that uses the Library" uses material from a header file that +is part of the Library, the object code for the work may be a derivative work +of the Library even though the source code is not. Whether this is true is +especially significant if the work can be linked without the Library, or if +the work is itself a library. The threshold for this to be true is not precisely +defined by law. + +If such an object file uses only numerical parameters, data structure layouts +and accessors, and small macros and small inline functions (ten lines or less +in length), then the use of the object file is unrestricted, regardless of +whether it is legally a derivative work. (Executables containing this object +code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute +the object code for the work under the terms of Section 6. Any executables +containing that work also fall under Section 6, whether or not they are linked +directly with the Library itself. + +6. As an exception to the Sections above, you may also compile or link a "work +that uses the Library" with the Library to produce a work containing portions +of the Library, and distribute that work under terms of your choice, provided +that the terms permit modification of the work for the customer's own use +and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library +is used in it and that the Library and its use are covered by this License. +You must supply a copy of this License. If the work during execution displays +copyright notices, you must include the copyright notice for the Library among +them, as well as a reference directing the user to the copy of this License. +Also, you must do one of these things: + +a) Accompany the work with the complete corresponding machine-readable source +code for the Library including whatever changes were used in the work (which +must be distributed under Sections 1 and 2 above); and, if the work is an +executable linked with the Library, with the complete machine-readable "work +that uses the Library", as object code and/or source code, so that the user +can modify the Library and then relink to produce a modified executable containing +the modified Library. (It is understood that the user who changes the contents +of definitions files in the Library will not necessarily be able to recompile +the application to use the modified definitions.) + +b) Accompany the work with a written offer, valid for at least three years, +to give the same user the materials specified in Subsection 6a, above, for +a charge no more than the cost of performing this distribution. + +c) If distribution of the work is made by offering access to copy from a designated +place, offer equivalent access to copy the above specified materials from +the same place. + +d) Verify that the user has already received a copy of these materials or +that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must +include any data and utility programs needed for reproducing the executable +from it. However, as a special exception, the 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. + +It may happen that this requirement contradicts the license restrictions of +other proprietary libraries that do not normally accompany the operating system. +Such a contradiction means you cannot use both them and the Library together +in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side +in a single library together with other library facilities not covered by +this License, and distribute such a combined library, provided that the separate +distribution of the work based on the Library and of the other library facilities +is otherwise permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work based on the +Library, uncombined with any other library facilities. This must be distributed +under the terms of the Sections above. + +b) Give prominent notice with the combined library of the fact that part of +it is a work based on the Library, and explaining where to find the accompanying +uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library +except as expressly provided under this License. Any attempt otherwise to +copy, modify, sublicense, link with, or distribute the Library is void, and +will automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will not +have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed +it. However, nothing else grants you permission to modify or distribute the +Library or its derivative works. These actions are prohibited by law if you +do not accept this License. Therefore, by modifying or distributing the Library +(or any work based on the Library), you indicate your acceptance of this License +to do so, and all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), +the recipient automatically receives a license from the original licensor +to copy, distribute, link with or modify the Library subject to these terms +and conditions. You may not impose any further restrictions on the recipients' +exercise of the rights granted herein. You are not responsible for enforcing +compliance by third parties to this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement +or for any other reason (not limited to patent issues), conditions are imposed +on you (whether by court order, agreement or otherwise) that contradict the +conditions of this License, they do not excuse you from the conditions of +this License. If you cannot distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, then as +a consequence you may not distribute the Library at all. For example, if a +patent license would not permit royalty-free redistribution of the Library +by all those who receive copies directly or indirectly through you, then the +only way you could satisfy both it and this License would be to refrain entirely +from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents +or other property right claims or to contest validity of any such claims; +this section has the sole purpose of protecting the integrity of the free +software distribution system which is implemented by public license practices. +Many people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose +that choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain +countries either by patents or by copyrighted interfaces, the original copyright +holder who places the Library under this License may add an explicit geographical +distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this +License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of +the Library General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to address +new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that version +or of any later version published by the Free Software Foundation. If the +Library does not specify a license version number, you may choose any version +ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs +whose distribution conditions are incompatible with these, write to the author +to ask for permission. For software which is copyrighted by the Free Software +Foundation, write to the Free Software Foundation; we sometimes make exceptions +for this. Our decision will be guided by the two goals of preserving the free +status of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY +"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE +OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE +THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE +OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA +OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES +OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH +HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible +use to the public, we recommend making it free software that everyone can +redistribute and change. You can do so by permitting redistribution under +these terms (or, alternatively, under the terms of the ordinary General Public +License). + +To apply these terms, attach the following notices to the library. It is safest +to attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the "copyright" +line and a pointer to where the full notice is found. + +one line to give the library's name and an idea of what it does. + +Copyright (C) year name of author + +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more +details. + +You should have received a copy of the GNU Library General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the library, if necessary. Here +is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in + +the library `Frob' (a library for tweaking knobs) written + +by James Random Hacker. + +signature of Ty Coon, 1 April 1990 + +Ty Coon, President of Vice + +That's all there is to it! diff --git a/autotests/davcollectiontest.cpp b/autotests/davcollectiontest.cpp index f326afd..06dbd63 100644 --- a/autotests/davcollectiontest.cpp +++ b/autotests/davcollectiontest.cpp @@ -1,110 +1,99 @@ /* - Copyright (c) 2017 Sandro Knauß + SPDX-FileCopyrightText: 2017 Sandro Knauß - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #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 index 0692641..de4a87c 100644 --- a/autotests/davcollectiontest.h +++ b/autotests/davcollectiontest.h @@ -1,34 +1,23 @@ /* - Copyright (c) 2017 Sandro Knauß + SPDX-FileCopyrightText: 2017 Sandro Knauß - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #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 index 50c46e9..485cada 100644 --- a/autotests/davitemfetchjobtest.cpp +++ b/autotests/davitemfetchjobtest.cpp @@ -1,64 +1,53 @@ /* - Copyright (c) 2017 Sandro Knauß + SPDX-FileCopyrightText: 2017 Sandro Knauß - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davitemfetchjobtest.h" #include "fakeserver.h" #include #include void DavItemFetchJobTest::initTestCase() { // To avoid a runtime dependency on klauncher qputenv("KDE_FORK_SLAVES", "yes"); // To let ctest exit, we shouldn't start kio_http_cache_cleaner qputenv("KIO_DISABLE_CACHE_CLEANER", "yes"); } 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 index 7c9fce3..e86f016 100644 --- a/autotests/davitemfetchjobtest.h +++ b/autotests/davitemfetchjobtest.h @@ -1,32 +1,21 @@ /* - Copyright (c) 2017 Sandro Knauß + SPDX-FileCopyrightText: 2017 Sandro Knauß - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef DAVITEMFETCHJOB_TEST_H #define DAVITEMFETCHJOB_TEST_H #include class DavItemFetchJobTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void runSuccessfullTest(); }; #endif diff --git a/autotests/davitemslistjobtest.cpp b/autotests/davitemslistjobtest.cpp index 89492ae..2786124 100644 --- a/autotests/davitemslistjobtest.cpp +++ b/autotests/davitemslistjobtest.cpp @@ -1,45 +1,34 @@ /* - Copyright (c) 2017 Sandro Knauß + SPDX-FileCopyrightText: 2017 Sandro Knauß - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #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 index e48cdba..87fa092 100644 --- a/autotests/davitemslistjobtest.h +++ b/autotests/davitemslistjobtest.h @@ -1,31 +1,20 @@ /* - Copyright (c) 2017 Sandro Knauß + SPDX-FileCopyrightText: 2017 Sandro Knauß - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #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 index 8519184..5bd1112 100644 --- a/autotests/davitemtest.cpp +++ b/autotests/davitemtest.cpp @@ -1,114 +1,103 @@ /* - Copyright (c) 2017 Sandro Knauß + SPDX-FileCopyrightText: 2017 Sandro Knauß - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #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 index 7fd1bd0..0ea589b 100644 --- a/autotests/davitemtest.h +++ b/autotests/davitemtest.h @@ -1,36 +1,25 @@ /* - Copyright (c) 2017 Sandro Knauß + SPDX-FileCopyrightText: 2017 Sandro Knauß - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #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 index 7d56399..d0af820 100644 --- a/autotests/davurltest.cpp +++ b/autotests/davurltest.cpp @@ -1,75 +1,64 @@ /* - Copyright (c) 2017 Sandro Knauß + SPDX-FileCopyrightText: 2017 Sandro Knauß - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #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 index 7a618f6..ecc7624 100644 --- a/autotests/davurltest.h +++ b/autotests/davurltest.h @@ -1,35 +1,24 @@ /* - Copyright (c) 2017 Sandro Knauß + SPDX-FileCopyrightText: 2017 Sandro Knauß - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #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 index 3af7cd2..5073b1d 100644 --- a/autotests/fakeserver.cpp +++ b/autotests/fakeserver.cpp @@ -1,223 +1,209 @@ /* - Copyright (C) 2008 Omat Holding B.V. + SPDX-FileCopyrightText: 2008 Omat Holding B .V. + SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB , a KDAB Group company + SPDX-FileContributor: Kevin Ottens + SPDX-FileCopyrightText: 2017 Sandro Kanuß - 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. + SPDX-License-Identifier: GPL-2.0-or-later */ // Own #include "fakeserver.h" // Qt #include #include #include #include -FakeServer::FakeServer(QObject *parent) +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 QMetaObject::invokeMethod(this, &FakeServer::started, Qt::BlockingQueuedConnection); } 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); for (const QList &scenario : qAsConst(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 index 244dcf7..f1a0589 100644 --- a/autotests/fakeserver.h +++ b/autotests/fakeserver.h @@ -1,195 +1,181 @@ /* - Copyright (C) 2008 Omat Holding B.V. + SPDX-FileCopyrightText: 2008 Omat Holding B .V. + SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB , a KDAB Group company + SPDX-FileContributor: Kevin Ottens + SPDX-FileCopyrightText: 2017 Sandro Kanuß - 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. + SPDX-License-Identifier: GPL-2.0-or-later */ #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 current 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/src/common/davcollection.cpp b/src/common/davcollection.cpp index 9ad5b94..ff68c1a 100644 --- a/src/common/davcollection.cpp +++ b/src/common/davcollection.cpp @@ -1,113 +1,102 @@ /* - Copyright (c) 2009 Grégory Oestreicher + SPDX-FileCopyrightText: 2009 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davcollection.h" #include "davurl.h" #include using namespace KDAV; class DavCollectionPrivate : public QSharedData { public: DavCollection::ContentTypes mContentTypes; QString mCTag; DavUrl mUrl; QString mDisplayName; QColor mColor; Privileges mPrivileges; }; DavCollection::DavCollection() : d(new DavCollectionPrivate) { } DavCollection::DavCollection(const DavUrl &url, const QString &displayName, ContentTypes contentTypes) : d(new DavCollectionPrivate) { d->mUrl = url; d->mDisplayName = displayName; d->mContentTypes = contentTypes; d->mPrivileges = KDAV::All; } DavCollection::DavCollection(const DavCollection &other) = default; DavCollection &DavCollection::operator=(const DavCollection &other) = default; DavCollection::~DavCollection() = default; 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 index dc28754..5e25aca 100644 --- a/src/common/davcollection.h +++ b/src/common/davcollection.h @@ -1,154 +1,143 @@ /* - Copyright (c) 2009 Grégory Oestreicher + SPDX-FileCopyrightText: 2009 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVCOLLECTION_H #define KDAV_DAVCOLLECTION_H #include "kdav_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 KDAV_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: QSharedDataPointer 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 index 33e1af7..41750f2 100644 --- a/src/common/davcollectiondeletejob.cpp +++ b/src/common/davcollectiondeletejob.cpp @@ -1,71 +1,60 @@ /* - Copyright (c) 2010 Grégory Oestreicher + SPDX-FileCopyrightText: 2010 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davcollectiondeletejob.h" #include "davjobbase_p.h" #include "daverror.h" #include #include using namespace KDAV; namespace KDAV { class DavCollectionDeleteJobPrivate : public DavJobBasePrivate { public: DavUrl mUrl; }; } DavCollectionDeleteJob::DavCollectionDeleteJob(const DavUrl &url, QObject *parent) : DavJobBase(new DavCollectionDeleteJobPrivate, parent) { Q_D(DavCollectionDeleteJob); d->mUrl = url; } void DavCollectionDeleteJob::start() { Q_D(DavCollectionDeleteJob); KIO::DeleteJob *job = KIO::del(d->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 index 69ba73f..d2d4362 100644 --- a/src/common/davcollectiondeletejob.h +++ b/src/common/davcollectiondeletejob.h @@ -1,62 +1,51 @@ /* - Copyright (c) 2010 Grégory Oestreicher + SPDX-FileCopyrightText: 2010 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVCOLLECTIONDELETEJOB_H #define KDAV_DAVCOLLECTIONDELETEJOB_H #include "kdav_export.h" #include "davjobbase.h" #include "davurl.h" #include namespace KDAV { class DavCollectionDeleteJobPrivate; /** * @short A job that deletes a DAV collection. * * This job is used to delete a DAV collection at a certain URL. */ class KDAV_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: Q_DECLARE_PRIVATE(DavCollectionDeleteJob) }; } #endif diff --git a/src/common/davcollectionmodifyjob.cpp b/src/common/davcollectionmodifyjob.cpp index 6bd4d8e..ca10441 100644 --- a/src/common/davcollectionmodifyjob.cpp +++ b/src/common/davcollectionmodifyjob.cpp @@ -1,175 +1,164 @@ /* - Copyright (c) 2010 Grégory Oestreicher + SPDX-FileCopyrightText: 2010 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davcollectionmodifyjob.h" #include "davjobbase_p.h" #include "davmanager_p.h" #include "daverror.h" #include "utils_p.h" #include #include using namespace KDAV; namespace KDAV { class DavCollectionModifyJobPrivate : public DavJobBasePrivate { public: DavUrl mUrl; QDomDocument mQuery; QVector mSetProperties; QVector mRemoveProperties; }; } DavCollectionModifyJob::DavCollectionModifyJob(const DavUrl &url, QObject *parent) : DavJobBase(new DavCollectionModifyJobPrivate, parent) { Q_D(DavCollectionModifyJob); d->mUrl = url; } void DavCollectionModifyJob::setProperty(const QString &prop, const QString &value, const QString &ns) { Q_D(DavCollectionModifyJob); QDomElement propElement; if (ns.isEmpty()) { propElement = d->mQuery.createElement(prop); } else { propElement = d->mQuery.createElementNS(ns, prop); } const QDomText textElement = d->mQuery.createTextNode(value); propElement.appendChild(textElement); d->mSetProperties << propElement; } void DavCollectionModifyJob::removeProperty(const QString &prop, const QString &ns) { Q_D(DavCollectionModifyJob); QDomElement propElement; if (ns.isEmpty()) { propElement = d->mQuery.createElement(prop); } else { propElement = d->mQuery.createElementNS(ns, prop); } d->mRemoveProperties << propElement; } void DavCollectionModifyJob::start() { Q_D(DavCollectionModifyJob); if (d->mSetProperties.isEmpty() && d->mRemoveProperties.isEmpty()) { setError(ERR_COLLECTIONMODIFY_NO_PROPERITES); setErrorTextFromDavError(); emitResult(); return; } QDomDocument mQuery; QDomElement propertyUpdateElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propertyupdate")); mQuery.appendChild(propertyUpdateElement); if (!d->mSetProperties.isEmpty()) { QDomElement setElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("set")); propertyUpdateElement.appendChild(setElement); QDomElement propElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); setElement.appendChild(propElement); for (const QDomElement &element : qAsConst(d->mSetProperties)) { propElement.appendChild(element); } } if (!d->mRemoveProperties.isEmpty()) { QDomElement removeElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("remove")); propertyUpdateElement.appendChild(removeElement); QDomElement propElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); removeElement.appendChild(propElement); for (const QDomElement &element : qAsConst(d->mSetProperties)) { propElement.appendChild(element); } } KIO::DavJob *job = DavManager::self()->createPropPatchJob(d->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 index 9d8cddc..84278c5 100644 --- a/src/common/davcollectionmodifyjob.h +++ b/src/common/davcollectionmodifyjob.h @@ -1,78 +1,67 @@ /* - Copyright (c) 2010 Grégory Oestreicher + SPDX-FileCopyrightText: 2010 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVCOLLECTIONMODIFYJOB_H #define KDAV_DAVCOLLECTIONMODIFYJOB_H #include "kdav_export.h" #include "davjobbase.h" #include "davurl.h" #include namespace KDAV { class DavCollectionModifyJobPrivate; /** * @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 KDAV_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); Q_DECLARE_PRIVATE(DavCollectionModifyJob) }; } #endif diff --git a/src/common/davcollectionsfetchjob.cpp b/src/common/davcollectionsfetchjob.cpp index 42ac849..59cad29 100644 --- a/src/common/davcollectionsfetchjob.cpp +++ b/src/common/davcollectionsfetchjob.cpp @@ -1,373 +1,362 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davcollectionsfetchjob.h" #include "davjobbase_p.h" #include "davmanager_p.h" #include "davprincipalhomesetsfetchjob.h" #include "davprotocolbase_p.h" #include "utils_p.h" #include "daverror.h" #include "libkdav_debug.h" #include #include #include #include #include using namespace KDAV; namespace KDAV { class DavCollectionsFetchJobPrivate : public DavJobBasePrivate { public: DavUrl mUrl; DavCollection::List mCollections; uint mSubJobCount = 0; }; } DavCollectionsFetchJob::DavCollectionsFetchJob(const DavUrl &url, QObject *parent) : DavJobBase(new DavCollectionsFetchJobPrivate, parent) { Q_D(DavCollectionsFetchJob); d->mUrl = url; } void DavCollectionsFetchJob::start() { Q_D(DavCollectionsFetchJob); if (DavManager::davProtocol(d->mUrl.protocol())->supportsPrincipals()) { DavPrincipalHomeSetsFetchJob *job = new DavPrincipalHomeSetsFetchJob(d->mUrl); connect(job, &DavPrincipalHomeSetsFetchJob::result, this, &DavCollectionsFetchJob::principalFetchFinished); job->start(); } else { doCollectionsFetch(d->mUrl.url()); } } DavCollection::List DavCollectionsFetchJob::collections() const { Q_D(const DavCollectionsFetchJob); return d->mCollections; } DavUrl DavCollectionsFetchJob::davUrl() const { Q_D(const DavCollectionsFetchJob); return d->mUrl; } void DavCollectionsFetchJob::doCollectionsFetch(const QUrl &url) { Q_D(DavCollectionsFetchJob); ++d->mSubJobCount; const QDomDocument collectionQuery = DavManager::davProtocol(d->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) { Q_D(DavCollectionsFetchJob); 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(d->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(d->mUrl.url()); return; } for (const QString &homeSet : homeSets) { QUrl url = d->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) { Q_D(DavCollectionsFetchJob); 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() != d->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(d->mUrl.url()); --d->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 = d->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::davProtocol(d->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(d->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::davProtocol(d->mUrl.protocol())->collectionContentTypes(propstatElement); auto _url = url; _url.setUserInfo(d->mUrl.url().userInfo()); DavCollection collection(DavUrl(_url, d->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(); d->mCollections << collection; Q_EMIT collectionDiscovered(d->mUrl.protocol(), url.toDisplayString(), jobUrl); responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response")); } } } subjobFinished(); } void DavCollectionsFetchJob::subjobFinished() { Q_D(DavCollectionsFetchJob); if (--d->mSubJobCount == 0) { emitResult(); } } diff --git a/src/common/davcollectionsfetchjob.h b/src/common/davcollectionsfetchjob.h index 4522f1e..ff2b894 100644 --- a/src/common/davcollectionsfetchjob.h +++ b/src/common/davcollectionsfetchjob.h @@ -1,85 +1,74 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVCOLLECTIONSFETCHJOB_H #define KDAV_DAVCOLLECTIONSFETCHJOB_H #include "kdav_export.h" #include "davcollection.h" #include "davjobbase.h" #include "davurl.h" #include namespace KDAV { class DavCollectionsFetchJobPrivate; /** * @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 KDAV_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(); Q_DECLARE_PRIVATE(DavCollectionsFetchJob) }; } #endif diff --git a/src/common/davcollectionsmultifetchjob.cpp b/src/common/davcollectionsmultifetchjob.cpp index df738e4..1d04c8b 100644 --- a/src/common/davcollectionsmultifetchjob.cpp +++ b/src/common/davcollectionsmultifetchjob.cpp @@ -1,77 +1,66 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davcollectionsmultifetchjob.h" #include "davcollectionsfetchjob.h" using namespace KDAV; namespace KDAV { class DavCollectionsMultiFetchJobPrivate { public: DavUrl::List mUrls; DavCollection::List mCollections; int mSubJobCount = -1; }; } DavCollectionsMultiFetchJob::DavCollectionsMultiFetchJob(const DavUrl::List &urls, QObject *parent) : KJob(parent) , d(new DavCollectionsMultiFetchJobPrivate) { d->mUrls = urls; d->mSubJobCount = urls.size(); } DavCollectionsMultiFetchJob::~DavCollectionsMultiFetchJob() = default; void DavCollectionsMultiFetchJob::start() { if (d->mUrls.isEmpty()) { emitResult(); } for (const DavUrl &url : qAsConst(d->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 d->mCollections; } void DavCollectionsMultiFetchJob::davJobFinished(KJob *job) { DavCollectionsFetchJob *fetchJob = qobject_cast(job); if (job->error()) { setError(job->error()); setErrorText(job->errorText()); } else { d->mCollections << fetchJob->collections(); } if (--d->mSubJobCount == 0) { emitResult(); } } diff --git a/src/common/davcollectionsmultifetchjob.h b/src/common/davcollectionsmultifetchjob.h index 9a38bd8..ccbc7d7 100644 --- a/src/common/davcollectionsmultifetchjob.h +++ b/src/common/davcollectionsmultifetchjob.h @@ -1,82 +1,71 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVCOLLECTIONSMULTIFETCHJOB_H #define KDAV_DAVCOLLECTIONSMULTIFETCHJOB_H #include "kdav_export.h" #include "davcollection.h" #include "davurl.h" #include #include namespace KDAV { class DavCollectionsMultiFetchJobPrivate; /** * @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 KDAV_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); ~DavCollectionsMultiFetchJob(); /** * 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 *); const std::unique_ptr d; }; } #endif diff --git a/src/common/daverror.cpp b/src/common/daverror.cpp index d07c5f0..e9a7764 100644 --- a/src/common/daverror.cpp +++ b/src/common/daverror.cpp @@ -1,163 +1,152 @@ /* - Copyright (c) 2016 Sandro Knauß + SPDX-FileCopyrightText: 2016 Sandro Knauß - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "daverror.h" #include #include #include using namespace KDAV; namespace KDAV { class ErrorPrivate : public QSharedData { public: ErrorNumber mErrorNumber = NO_ERR; int mResponseCode = 0; int mJobErrorCode = 0; QString mErrorText; }; } Error::Error() : d(new ErrorPrivate) { } Error::Error(ErrorNumber errNo, int responseCode, const QString &errorText, int jobErrorCode) : d(new ErrorPrivate) { d->mErrorNumber = errNo; d->mResponseCode = responseCode; d->mErrorText = errorText; d->mJobErrorCode = jobErrorCode; } Error::Error(const Error&) = default; Error::~Error() = default; Error& Error::operator=(const Error&) = default; ErrorNumber Error::errorNumber() const { return d->mErrorNumber; } QString Error::internalErrorText() const { return d->mErrorText; } int Error::jobErrorCode() const { return d->mJobErrorCode; } int Error::responseCode() const { return d->mResponseCode; } QString KDAV::Error::translatedJobError() const { QString err; if (d->mJobErrorCode > 0 && d->mJobErrorCode != KIO::ERR_SLAVE_DEFINED) { err = KIO::buildErrorString(d->mJobErrorCode, d->mErrorText); } else { err = d->mErrorText; } return err; } QString Error::errorText() const { QString result; QString err = translatedJobError(); switch (d->mErrorNumber) { case ERR_PROBLEM_WITH_REQUEST: // User-side error if (d->mResponseCode == 401) { err = i18n("Invalid username/password"); } else if (d->mResponseCode == 403) { err = i18n("Access forbidden"); } else if (d->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, d->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, d->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, d->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, d->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 (!d->mErrorText.isEmpty()) { result.append(i18n("\nThe server returned more information:\n%1", d->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, d->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, d->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, d->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 index bc9532a..9430c7b 100644 --- a/src/common/daverror.h +++ b/src/common/daverror.h @@ -1,73 +1,62 @@ /* - Copyright (c) 2016 Sandro Knauß + SPDX-FileCopyrightText: 2016 Sandro Knauß - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVERROR_H #define KDAV_DAVERROR_H #include "kdav_export.h" #include #include #include namespace KDAV { /** DAV operation error codes. */ 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 ErrorPrivate; /** DAV operation error. */ class KDAV_EXPORT Error { public: explicit Error(); explicit Error(ErrorNumber errNo, int responseCode, const QString &errorText, int jobErrorCode); Error(const Error&); ~Error(); Error& operator=(const Error&); 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: QSharedDataPointer d; }; } #endif diff --git a/src/common/davitem.cpp b/src/common/davitem.cpp index 31be108..723270e 100644 --- a/src/common/davitem.cpp +++ b/src/common/davitem.cpp @@ -1,115 +1,104 @@ /* - Copyright (c) 2009 Grégory Oestreicher + SPDX-FileCopyrightText: 2009 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davitem.h" #include "davurl.h" using namespace KDAV; class DavItemPrivate : public QSharedData { public: DavUrl mUrl; QString mContentType; QByteArray mData; QString mEtag; }; DavItem::DavItem() : d(new DavItemPrivate) { } DavItem::DavItem(const DavUrl &url, const QString &contentType, const QByteArray &data, const QString &etag) : d(new DavItemPrivate) { d->mUrl = url; d->mContentType = contentType; d->mData = data; d->mEtag = etag; } DavItem::DavItem(const DavItem &other) = default; DavItem &DavItem::operator=(const DavItem &other) = default; DavItem::~DavItem() = default; 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 index 8691421..0810d76 100644 --- a/src/common/davitem.h +++ b/src/common/davitem.h @@ -1,123 +1,112 @@ /* - Copyright (c) 2009 Grégory Oestreicher + SPDX-FileCopyrightText: 2009 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVITEM_H #define KDAV_DAVITEM_H #include "kdav_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 KDAV_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: QSharedDataPointer d; }; KDAV_EXPORT QDataStream &operator<<(QDataStream &out, const DavItem &item); KDAV_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 index fa8fb45..85a7b23 100644 --- a/src/common/davitemcreatejob.cpp +++ b/src/common/davitemcreatejob.cpp @@ -1,149 +1,138 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davitemcreatejob.h" #include "davjobbase_p.h" #include "davitemfetchjob.h" #include "davmanager_p.h" #include "daverror.h" #include #include #include "libkdav_debug.h" using namespace KDAV; namespace KDAV { class DavItemCreateJobPrivate : public DavJobBasePrivate { public: DavItem mItem; int mRedirectCount = 0; }; } DavItemCreateJob::DavItemCreateJob(const DavItem &item, QObject *parent) : DavJobBase(new DavItemCreateJobPrivate, parent) { Q_D(DavItemCreateJob); d->mItem = item; } void DavItemCreateJob::start() { Q_D(DavItemCreateJob); QString headers = QStringLiteral("Content-Type: "); headers += d->mItem.contentType(); headers += QLatin1String("\r\n"); headers += QLatin1String("If-None-Match: *"); KIO::StoredTransferJob *job = KIO::storedPut(d->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 { Q_D(const DavItemCreateJob); return d->mItem; } QUrl DavItemCreateJob::itemUrl() const { Q_D(const DavItemCreateJob); return d->mItem.url().url(); } void DavItemCreateJob::davJobFinished(KJob *job) { Q_D(DavItemCreateJob); 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; 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); } if (responseCode == 301 || responseCode == 302 || responseCode == 307 || responseCode == 308) { if (d->mRedirectCount > 4) { setLatestResponseCode(responseCode); setError(UserDefinedError + responseCode); emitResult(); } else { QUrl _itemUrl(url); _itemUrl.setUserInfo(itemUrl().userInfo()); d->mItem.setUrl(DavUrl(_itemUrl, d->mItem.url().protocol())); ++d->mRedirectCount; start(); } return; } url.setUserInfo(itemUrl().userInfo()); d->mItem.setUrl(DavUrl(url, d->mItem.url().protocol())); DavItemFetchJob *fetchJob = new DavItemFetchJob(d->mItem); connect(fetchJob, &DavItemFetchJob::result, this, &DavItemCreateJob::itemRefreshed); fetchJob->start(); } void DavItemCreateJob::itemRefreshed(KJob *job) { Q_D(DavItemCreateJob); if (!job->error()) { DavItemFetchJob *fetchJob = qobject_cast(job); d->mItem.setEtag(fetchJob->item().etag()); } emitResult(); } diff --git a/src/common/davitemcreatejob.h b/src/common/davitemcreatejob.h index 7027111..c3f93a0 100644 --- a/src/common/davitemcreatejob.h +++ b/src/common/davitemcreatejob.h @@ -1,67 +1,56 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVITEMCREATEJOB_H #define KDAV_DAVITEMCREATEJOB_H #include "kdav_export.h" #include "davitem.h" #include "davjobbase.h" #include "davurl.h" namespace KDAV { class DavItemCreateJobPrivate; /** * @short A job to create a DAV item on the DAV server. */ class KDAV_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. */ explicit 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 *); Q_DECLARE_PRIVATE(DavItemCreateJob) }; } #endif diff --git a/src/common/davitemdeletejob.cpp b/src/common/davitemdeletejob.cpp index 9d7dfdf..d0e3ae2 100644 --- a/src/common/davitemdeletejob.cpp +++ b/src/common/davitemdeletejob.cpp @@ -1,111 +1,100 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davitemdeletejob.h" #include "davjobbase_p.h" #include "davitemfetchjob.h" #include "davmanager_p.h" #include "daverror.h" #include #include using namespace KDAV; namespace KDAV { class DavItemDeleteJobPrivate : public DavJobBasePrivate { public: DavItem mItem; DavItem mFreshItem; int mFreshResponseCode = -1; }; } DavItemDeleteJob::DavItemDeleteJob(const DavItem &item, QObject *parent) : DavJobBase(new DavItemDeleteJobPrivate, parent) { Q_D(DavItemDeleteJob); d->mItem = item; } void DavItemDeleteJob::start() { Q_D(DavItemDeleteJob); KIO::DeleteJob *job = KIO::del(d->mItem.url().url(), KIO::HideProgressInfo | KIO::DefaultFlags); job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); job->addMetaData(QStringLiteral("customHTTPHeader"), QStringLiteral("If-Match: ") + d->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 { Q_D(const DavItemDeleteJob); return d->mFreshItem; } int DavItemDeleteJob::freshResponseCode() const { Q_D(const DavItemDeleteJob); return d->mFreshResponseCode; } void DavItemDeleteJob::davJobFinished(KJob *job) { Q_D(DavItemDeleteJob); 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(d->mItem); connect(fetchJob, &DavItemFetchJob::result, this, &DavItemDeleteJob::conflictingItemFetched); fetchJob->start(); return; } } emitResult(); } void DavItemDeleteJob::conflictingItemFetched(KJob *job) { Q_D(DavItemDeleteJob); DavItemFetchJob *fetchJob = qobject_cast(job); d->mFreshResponseCode = fetchJob->latestResponseCode(); if (!job->error()) { d->mFreshItem = fetchJob->item(); } emitResult(); } diff --git a/src/common/davitemdeletejob.h b/src/common/davitemdeletejob.h index f2ee8f8..770fc5a 100644 --- a/src/common/davitemdeletejob.h +++ b/src/common/davitemdeletejob.h @@ -1,69 +1,58 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVITEMDELETEJOB_H #define KDAV_DAVITEMDELETEJOB_H #include "kdav_export.h" #include "davitem.h" #include "davjobbase.h" #include "davurl.h" namespace KDAV { class DavItemDeleteJobPrivate; /** * @short A job to delete a DAV item on the DAV server. */ class KDAV_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. */ explicit 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 *); Q_DECLARE_PRIVATE(DavItemDeleteJob) }; } #endif diff --git a/src/common/davitemfetchjob.cpp b/src/common/davitemfetchjob.cpp index 1721e64..765c1bc 100644 --- a/src/common/davitemfetchjob.cpp +++ b/src/common/davitemfetchjob.cpp @@ -1,103 +1,92 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davitemfetchjob.h" #include "davjobbase_p.h" #include "davmanager_p.h" #include "daverror.h" #include #include using namespace KDAV; namespace KDAV { class DavItemFetchJobPrivate : public DavJobBasePrivate { public: DavUrl mUrl; DavItem mItem; }; } 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(new DavItemFetchJobPrivate, parent) { Q_D(DavItemFetchJob); d->mItem = item; } void DavItemFetchJob::start() { Q_D(DavItemFetchJob); KIO::StoredTransferJob *job = KIO::storedGet(d->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 { Q_D(const DavItemFetchJob); return d->mItem; } void DavItemFetchJob::davJobFinished(KJob *job) { Q_D(DavItemFetchJob); 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 { d->mItem.setData(storedJob->data()); d->mItem.setContentType(storedJob->queryMetaData(QStringLiteral("content-type"))); d->mItem.setEtag(etagFromHeaders(storedJob->queryMetaData(QStringLiteral("HTTP-Headers")))); } emitResult(); } diff --git a/src/common/davitemfetchjob.h b/src/common/davitemfetchjob.h index 20f850d..bc32e78 100644 --- a/src/common/davitemfetchjob.h +++ b/src/common/davitemfetchjob.h @@ -1,62 +1,51 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVITEMFETCHJOB_H #define KDAV_DAVITEMFETCHJOB_H #include "kdav_export.h" #include "davitem.h" #include "davjobbase.h" #include "davurl.h" namespace KDAV { class DavItemFetchJobPrivate; /** * @short A job that fetches a DAV item from the DAV server. */ class KDAV_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. */ explicit 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 *); Q_DECLARE_PRIVATE(DavItemFetchJob) }; } #endif diff --git a/src/common/davitemmodifyjob.cpp b/src/common/davitemmodifyjob.cpp index 4b69400..f045218 100644 --- a/src/common/davitemmodifyjob.cpp +++ b/src/common/davitemmodifyjob.cpp @@ -1,163 +1,152 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davitemmodifyjob.h" #include "davjobbase_p.h" #include "davitemfetchjob.h" #include "davmanager_p.h" #include "daverror.h" #include using namespace KDAV; namespace KDAV { class DavItemModifyJobPrivate : public DavJobBasePrivate { public: DavItem mItem; DavItem mFreshItem; int mFreshResponseCode = 0; }; } DavItemModifyJob::DavItemModifyJob(const DavItem &item, QObject *parent) : DavJobBase(new DavItemModifyJobPrivate, parent) { Q_D(DavItemModifyJob); d->mItem = item; } void DavItemModifyJob::start() { Q_D(DavItemModifyJob); QString headers = QStringLiteral("Content-Type: "); headers += d->mItem.contentType(); headers += QLatin1String("\r\n"); headers += QLatin1String("If-Match: ") + d->mItem.etag(); KIO::StoredTransferJob *job = KIO::storedPut(d->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 { Q_D(const DavItemModifyJob); return d->mItem; } DavItem DavItemModifyJob::freshItem() const { Q_D(const DavItemModifyJob); return d->mFreshItem; } int DavItemModifyJob::freshResponseCode() const { Q_D(const DavItemModifyJob); return d->mFreshResponseCode; } QUrl DavItemModifyJob::itemUrl() const { Q_D(const DavItemModifyJob); return d->mItem.url().url(); } void DavItemModifyJob::davJobFinished(KJob *job) { Q_D(DavItemModifyJob); 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(d->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()); d->mItem.setUrl(DavUrl(url, d->mItem.url().protocol())); DavItemFetchJob *fetchJob = new DavItemFetchJob(d->mItem); connect(fetchJob, &DavItemFetchJob::result, this, &DavItemModifyJob::itemRefreshed); fetchJob->start(); } void DavItemModifyJob::itemRefreshed(KJob *job) { Q_D(DavItemModifyJob); if (!job->error()) { DavItemFetchJob *fetchJob = qobject_cast(job); d->mItem.setEtag(fetchJob->item().etag()); } else { d->mItem.setEtag(QString()); } emitResult(); } void DavItemModifyJob::conflictingItemFetched(KJob *job) { Q_D(DavItemModifyJob); DavItemFetchJob *fetchJob = qobject_cast(job); d->mFreshResponseCode = fetchJob->latestResponseCode(); if (!job->error()) { d->mFreshItem = fetchJob->item(); } emitResult(); } diff --git a/src/common/davitemmodifyjob.h b/src/common/davitemmodifyjob.h index 61f4d9d..0429dc9 100644 --- a/src/common/davitemmodifyjob.h +++ b/src/common/davitemmodifyjob.h @@ -1,77 +1,66 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVITEMMODIFYJOB_H #define KDAV_DAVITEMMODIFYJOB_H #include "kdav_export.h" #include "davitem.h" #include "davjobbase.h" #include "davurl.h" namespace KDAV { class DavItemModifyJobPrivate; /** * @short A job that modifies a DAV item on the DAV server. */ class KDAV_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. */ explicit 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 *); Q_DECLARE_PRIVATE(DavItemModifyJob) }; } #endif diff --git a/src/common/davitemsfetchjob.cpp b/src/common/davitemsfetchjob.cpp index 005fd59..859ad3f 100644 --- a/src/common/davitemsfetchjob.cpp +++ b/src/common/davitemsfetchjob.cpp @@ -1,177 +1,168 @@ /* - Copyright (c) 2010 Grégory Oestreicher - Based on DavItemsListJob which is copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. + Based on DavItemsListJob: + SPDX-FileCopyrightText: 2010 Tobias Koenig - 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davitemsfetchjob.h" #include "davjobbase_p.h" #include "davmanager_p.h" #include "davmultigetprotocol_p.h" #include "utils_p.h" #include "daverror.h" #include #include using namespace KDAV; namespace KDAV { class DavItemsFetchJobPrivate : public DavJobBasePrivate { public: DavUrl mCollectionUrl; QStringList mUrls; QMap mItems; }; } DavItemsFetchJob::DavItemsFetchJob(const DavUrl &collectionUrl, const QStringList &urls, QObject *parent) : DavJobBase(new DavItemsFetchJobPrivate, parent) { Q_D(DavItemsFetchJob); d->mCollectionUrl = collectionUrl; d->mUrls = urls; } void DavItemsFetchJob::start() { Q_D(DavItemsFetchJob); const DavMultigetProtocol *protocol = dynamic_cast(DavManager::davProtocol(d->mCollectionUrl.protocol())); if (!protocol) { setError(ERR_NO_MULTIGET); setErrorTextFromDavError(); emitResult(); return; } const QDomDocument report = protocol->itemsReportQuery(d->mUrls)->buildQuery(); KIO::DavJob *job = DavManager::self()->createReportJob(d->mCollectionUrl.url(), report, QStringLiteral("0")); job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); connect(job, &KIO::DavJob::result, this, &DavItemsFetchJob::davJobFinished); } DavItem::List DavItemsFetchJob::items() const { Q_D(const DavItemsFetchJob); DavItem::List values; values.reserve(d->mItems.size()); for (const auto &value : qAsConst(d->mItems)) { values << value; } return values; } DavItem DavItemsFetchJob::item(const QString &url) const { Q_D(const DavItemsFetchJob); return d->mItems.value(url); } void DavItemsFetchJob::davJobFinished(KJob *job) { Q_D(DavItemsFetchJob); 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::davProtocol(d->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(d->mCollectionUrl.url().userInfo()); item.setUrl(DavUrl(_url, d->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); d->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 index 7e25f59..83284bd 100644 --- a/src/common/davitemsfetchjob.h +++ b/src/common/davitemsfetchjob.h @@ -1,72 +1,63 @@ /* - Copyright (c) 2010 Grégory Oestreicher - Based on DavItemsListJob which is copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published by - the Free Software Foundation; either version 2 of the License, or (at your - option) any later version. + Based on DavItemsListJob: + SPDX-FileCopyrightText: 2010 Tobias Koenig - 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVITEMSFETCHJOB_H #define KDAV_DAVITEMSFETCHJOB_H #include "kdav_export.h" #include "davitem.h" #include "davjobbase.h" #include "davurl.h" #include #include namespace KDAV { class DavItemsFetchJobPrivate; /** * @short A job that fetches a list of items from a DAV server using a multiget query. */ class KDAV_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 *); Q_DECLARE_PRIVATE(DavItemsFetchJob) }; } #endif diff --git a/src/common/davitemslistjob.cpp b/src/common/davitemslistjob.cpp index 4a76241..eb0c59b 100644 --- a/src/common/davitemslistjob.cpp +++ b/src/common/davitemslistjob.cpp @@ -1,271 +1,260 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davitemslistjob.h" #include "davjobbase_p.h" #include "daverror.h" #include "davmanager_p.h" #include "davprotocolbase_p.h" #include "davurl.h" #include "utils_p.h" #include "etagcache.h" #include #include #include using namespace KDAV; namespace KDAV { class DavItemsListJobPrivate : public DavJobBasePrivate { public: 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 = 0; }; } DavItemsListJob::DavItemsListJob(const DavUrl &url, const std::shared_ptr &cache, QObject *parent) : DavJobBase(new DavItemsListJobPrivate, parent) { Q_D(DavItemsListJob); d->mUrl = url; d->mEtagCache = cache; } DavItemsListJob::~DavItemsListJob() = default; void DavItemsListJob::setContentMimeTypes(const QStringList &types) { Q_D(DavItemsListJob); d->mMimeTypes = types; } void DavItemsListJob::setTimeRange(const QString &start, const QString &end) { Q_D(DavItemsListJob); d->mRangeStart = start; d->mRangeEnd = end; } void DavItemsListJob::start() { Q_D(DavItemsListJob); const DavProtocolBase *protocol = DavManager::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 { Q_D(const DavItemsListJob); return d->mItems; } DavItem::List DavItemsListJob::changedItems() const { Q_D(const DavItemsListJob); return d->mChangedItems; } QStringList DavItemsListJob::deletedItems() const { Q_D(const DavItemsListJob); return d->mDeletedItems; } void DavItemsListJob::davJobFinished(KJob *job) { Q_D(DavItemsListJob); 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")); } } const auto etagCacheUrls = d->mEtagCache->urls(); #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) QSet removed = etagCacheUrls.toSet(); #else QSet removed(etagCacheUrls.begin(), etagCacheUrls.end()); #endif removed.subtract(d->mSeenUrls); d->mDeletedItems = removed.values(); if (--d->mSubJobCount == 0) { emitResult(); } } diff --git a/src/common/davitemslistjob.h b/src/common/davitemslistjob.h index 8deeb4e..b738aa6 100644 --- a/src/common/davitemslistjob.h +++ b/src/common/davitemslistjob.h @@ -1,98 +1,87 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVITEMSLISTJOB_H #define KDAV_DAVITEMSLISTJOB_H #include "kdav_export.h" #include "davitem.h" #include "davjobbase.h" #include #include namespace KDAV { class EtagCache; class DavUrl; class DavItemsListJobPrivate; /** * @short A job that lists all DAV items inside a DAV collection. */ class KDAV_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 *); Q_DECLARE_PRIVATE(DavItemsListJob) }; } #endif diff --git a/src/common/davjobbase.cpp b/src/common/davjobbase.cpp index 2614c76..26c3f3a 100644 --- a/src/common/davjobbase.cpp +++ b/src/common/davjobbase.cpp @@ -1,121 +1,110 @@ /* - Copyright (c) 2014 Gregory Oestreicher + SPDX-FileCopyrightText: 2014 Gregory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davjobbase.h" #include "davjobbase_p.h" #include "daverror.h" using namespace KDAV; DavJobBase::DavJobBase(QObject *parent) : KJob(parent) , d_ptr(new DavJobBasePrivate()) { } DavJobBase::DavJobBase(DavJobBasePrivate *dd, QObject *parent) : KJob(parent) , d_ptr(dd) { } DavJobBase::~DavJobBase() = default; int DavJobBase::latestResponseCode() const { return d_ptr->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_ptr->mLatestResponseCode = code; } Error DavJobBase::davError() const { return Error(static_cast(error()), d_ptr->mLatestResponseCode, d_ptr->mInternalErrorText, d_ptr->mJobErrorCode); } void DavJobBase::setJobErrorText(const QString &errorText) { d_ptr->mInternalErrorText = errorText; } void DavJobBase::setJobError(int jobErrorCode) { d_ptr->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 index 12aaec8..c1b375c 100644 --- a/src/common/davjobbase.h +++ b/src/common/davjobbase.h @@ -1,105 +1,94 @@ /* - Copyright (c) 2014 Gregory Oestreicher + SPDX-FileCopyrightText: 2014 Gregory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVJOBBASE_H #define KDAV_DAVJOBBASE_H #include "kdav_export.h" #include #include class DavJobBasePrivate; namespace KDAV { class Error; /** * @short base class for the jobs used by the resource. */ class KDAV_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); explicit DavJobBase(DavJobBasePrivate *dd, QObject *parent = nullptr); QScopedPointer d_ptr; private: Q_DECLARE_PRIVATE(DavJobBase) }; } #endif diff --git a/src/common/davjobbase_p.h b/src/common/davjobbase_p.h index a274a5e..7a67894 100644 --- a/src/common/davjobbase_p.h +++ b/src/common/davjobbase_p.h @@ -1,35 +1,24 @@ /* - Copyright (c) 2014 Gregory Oestreicher + SPDX-FileCopyrightText: 2014 Gregory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVJOBBASE_P_H #define KDAV_DAVJOBBASE_P_H #include #include class DavJobBasePrivate { public: virtual ~DavJobBasePrivate() = default; int mLatestResponseCode = 0; int mJobErrorCode = 0; QString mInternalErrorText; }; #endif diff --git a/src/common/davmanager.cpp b/src/common/davmanager.cpp index 569abcb..6a77344 100644 --- a/src/common/davmanager.cpp +++ b/src/common/davmanager.cpp @@ -1,95 +1,84 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davmanager_p.h" #include "protocols/caldavprotocol_p.h" #include "protocols/carddavprotocol_p.h" #include "protocols/groupdavprotocol_p.h" #include #include "libkdav_debug.h" #include #include using namespace KDAV; DavManager::DavManager() = default; DavManager::~DavManager() = default; DavManager *DavManager::self() { static DavManager sSelf; return &sSelf; } 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); job->addMetaData(QStringLiteral("cookies"), QStringLiteral("none")); job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); job->setProperty("davDepth", 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); job->addMetaData(QStringLiteral("cookies"), QStringLiteral("none")); job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); job->setProperty("davDepth", 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) { const auto d = DavManager::self(); if (!d->mProtocols[protocol]) { switch (protocol) { case KDAV::CalDav: d->mProtocols[KDAV::CalDav].reset(new CaldavProtocol()); break; case KDAV::CardDav: d->mProtocols[KDAV::CardDav].reset(new CarddavProtocol()); break; case KDAV::GroupDav: d->mProtocols[KDAV::GroupDav].reset(new GroupdavProtocol()); break; default: qCCritical(KDAV_LOG) << "Unknown protocol: " << static_cast(protocol); return nullptr; } } return d->mProtocols[protocol].get(); } diff --git a/src/common/davmanager_p.h b/src/common/davmanager_p.h index eb4162a..5e67d3a 100644 --- a/src/common/davmanager_p.h +++ b/src/common/davmanager_p.h @@ -1,101 +1,90 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVMANAGER_P_H #define KDAV_DAVMANAGER_P_H #include "enums.h" #include #include #include namespace KIO { class DavJob; } class QUrl; class QDomDocument; /** CalDav/CardDav protocol implementation. */ 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 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. */ static const DavProtocolBase *davProtocol(Protocol protocol); private: /** * Creates a new DAV manager. */ DavManager(); std::unique_ptr mProtocols[3]; }; } #endif diff --git a/src/common/davmultigetprotocol.cpp b/src/common/davmultigetprotocol.cpp index ac597d7..3323a37 100644 --- a/src/common/davmultigetprotocol.cpp +++ b/src/common/davmultigetprotocol.cpp @@ -1,24 +1,13 @@ /* - Copyright (c) 2010 Grégory Oestreicher + SPDX-FileCopyrightText: 2010 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davmultigetprotocol_p.h" using namespace KDAV; DavMultigetProtocol::~DavMultigetProtocol() { } diff --git a/src/common/davmultigetprotocol_p.h b/src/common/davmultigetprotocol_p.h index cc5af14..0cbc5f4 100644 --- a/src/common/davmultigetprotocol_p.h +++ b/src/common/davmultigetprotocol_p.h @@ -1,55 +1,44 @@ /* - Copyright (c) 2010 Grégory Oestreicher + SPDX-FileCopyrightText: 2010 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVMULTIGETPROTOCOL_H #define KDAV_DAVMULTIGETPROTOCOL_H #include "kdav_export.h" #include "davprotocolbase_p.h" namespace KDAV { /** * @short Base class for protocols that implement multiget capabilities */ class 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 index efe0b70..f0e8867 100644 --- a/src/common/davprincipalhomesetsfetchjob.cpp +++ b/src/common/davprincipalhomesetsfetchjob.cpp @@ -1,242 +1,231 @@ /* - Copyright (c) 2010 Grégory Oestreicher + SPDX-FileCopyrightText: 2010 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davprincipalhomesetsfetchjob.h" #include "davjobbase_p.h" #include "davmanager_p.h" #include "davprotocolbase_p.h" #include "daverror.h" #include "protocolinfo.h" #include "utils_p.h" #include #include using namespace KDAV; namespace KDAV { class DavPrincipalHomeSetsFetchJobPrivate : public DavJobBasePrivate { public: DavUrl mUrl; QStringList mHomeSets; }; } DavPrincipalHomeSetsFetchJob::DavPrincipalHomeSetsFetchJob(const DavUrl &url, QObject *parent) : DavJobBase(new DavPrincipalHomeSetsFetchJobPrivate, parent) { Q_D(DavPrincipalHomeSetsFetchJob); d->mUrl = url; } void DavPrincipalHomeSetsFetchJob::start() { fetchHomeSets(false); } void DavPrincipalHomeSetsFetchJob::fetchHomeSets(bool homeSetsOnly) { Q_D(DavPrincipalHomeSetsFetchJob); 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 = ProtocolInfo::principalHomeSet(d->mUrl.protocol()); const QString homeSetNS = ProtocolInfo::principalHomeSetNS(d->mUrl.protocol()); 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(d->mUrl.url(), document, QStringLiteral("0")); job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); connect(job, &KIO::DavJob::result, this, &DavPrincipalHomeSetsFetchJob::davJobFinished); } QStringList DavPrincipalHomeSetsFetchJob::homeSets() const { Q_D(const DavPrincipalHomeSetsFetchJob); return d->mHomeSets; } void DavPrincipalHomeSetsFetchJob::davJobFinished(KJob *job) { Q_D(DavPrincipalHomeSetsFetchJob); 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 = ProtocolInfo::principalHomeSet(d->mUrl.protocol()); const QString homeSetNS = ProtocolInfo::principalHomeSetNS(d->mUrl.protocol()); 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 (!d->mHomeSets.contains(href)) { d->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 (!d->mHomeSets.isEmpty() || nextRoundHref.isEmpty()) { emitResult(); } else { QUrl nextRoundUrl(d->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(d->mUrl.url().userName()); nextRoundUrl.setPassword(d->mUrl.url().password()); } d->mUrl.setUrl(nextRoundUrl); // And one more round, fetching only homesets fetchHomeSets(true); } } diff --git a/src/common/davprincipalhomesetsfetchjob.h b/src/common/davprincipalhomesetsfetchjob.h index 0515b09..aafe281 100644 --- a/src/common/davprincipalhomesetsfetchjob.h +++ b/src/common/davprincipalhomesetsfetchjob.h @@ -1,79 +1,68 @@ /* - Copyright (c) 2010 Grégory Oestreicher + SPDX-FileCopyrightText: 2010 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVPRINCIPALHOMESETSFETCHJOB_H #define KDAV_DAVPRINCIPALHOMESETSFETCHJOB_H #include "kdav_export.h" #include "davjobbase.h" #include "davurl.h" #include #include namespace KDAV { class DavPrincipalHomeSetsFetchJobPrivate; /** * @short A job that fetches home sets for a principal. */ class KDAV_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); Q_DECLARE_PRIVATE(DavPrincipalHomeSetsFetchJob) }; } #endif diff --git a/src/common/davprincipalsearchjob.cpp b/src/common/davprincipalsearchjob.cpp index 7b02070..49946ca 100644 --- a/src/common/davprincipalsearchjob.cpp +++ b/src/common/davprincipalsearchjob.cpp @@ -1,397 +1,386 @@ /* - Copyright (c) 2011 Grégory Oestreicher + SPDX-FileCopyrightText: 2011 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davprincipalsearchjob.h" #include "davjobbase_p.h" #include "davmanager_p.h" #include "utils_p.h" #include "daverror.h" #include #include #include using namespace KDAV; namespace KDAV { class DavPrincipalSearchJobPrivate : public DavJobBasePrivate { public: void buildReportQuery(QDomDocument &query) const; DavUrl mUrl; DavPrincipalSearchJob::FilterType mType; QString mFilter; int mPrincipalPropertySearchSubJobCount = 0; bool mPrincipalPropertySearchSubJobSuccessful = false; QList< QPair > mFetchProperties; QVector mResults; }; } DavPrincipalSearchJob::DavPrincipalSearchJob(const DavUrl &url, DavPrincipalSearchJob::FilterType type, const QString &filter, QObject *parent) : DavJobBase(new DavPrincipalSearchJobPrivate, parent) { Q_D(DavPrincipalSearchJob); d->mUrl = url; d->mType = type; d->mFilter = filter; } void DavPrincipalSearchJob::fetchProperty(const QString &name, const QString &ns) { Q_D(DavPrincipalSearchJob); QString propNamespace = ns; if (propNamespace.isEmpty()) { propNamespace = QStringLiteral("DAV:"); } d->mFetchProperties << QPair(propNamespace, name); } DavUrl DavPrincipalSearchJob::davUrl() const { Q_D(const DavPrincipalSearchJob); return d->mUrl; } void DavPrincipalSearchJob::start() { Q_D(DavPrincipalSearchJob); /* * 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(d->mUrl.url(), query); job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); connect(job, &KIO::DavJob::result, this, &DavPrincipalSearchJob::principalCollectionSetSearchFinished); job->start(); } void DavPrincipalSearchJob::principalCollectionSetSearchFinished(KJob *job) { Q_D(DavPrincipalSearchJob); 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 = d->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; d->buildReportQuery(principalPropertySearchQuery); KIO::DavJob *reportJob = DavManager::self()->createReportJob(url, principalPropertySearchQuery); reportJob->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); connect(reportJob, &KIO::DavJob::result, this, &DavPrincipalSearchJob::principalPropertySearchFinished); ++d->mPrincipalPropertySearchSubJobCount; reportJob->start(); } } void DavPrincipalSearchJob::principalPropertySearchFinished(KJob *job) { Q_D(DavPrincipalSearchJob); --d->mPrincipalPropertySearchSubJobCount; if (job->error() && !d->mPrincipalPropertySearchSubJobSuccessful) { setError(job->error()); setErrorText(job->errorText()); if (d->mPrincipalPropertySearchSubJobCount == 0) { emitResult(); } return; } KIO::DavJob *davJob = qobject_cast(job); const int responseCode = davJob->queryMetaData(QStringLiteral("responsecode")).toInt(); if (responseCode > 499 && responseCode < 600 && !d->mPrincipalPropertySearchSubJobSuccessful) { // Server-side error, unrecoverable setLatestResponseCode(responseCode); setError(ERR_SERVER_UNRECOVERABLE); setJobErrorText(davJob->errorText()); setJobError(davJob->error()); setErrorTextFromDavError(); if (d->mPrincipalPropertySearchSubJobCount == 0) { emitResult(); } return; } else if (responseCode > 399 && responseCode < 500 && !d->mPrincipalPropertySearchSubJobSuccessful) { setLatestResponseCode(responseCode); setError(ERR_PROBLEM_WITH_REQUEST); setJobErrorText(davJob->errorText()); setJobError(davJob->error()); setErrorTextFromDavError(); if (d->mPrincipalPropertySearchSubJobCount == 0) { emitResult(); } return; } if (!d->mPrincipalPropertySearchSubJobSuccessful) { setError(0); // nope, everything went fine d->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 (d->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 (d->mPrincipalPropertySearchSubJobCount == 0) { emitResult(); } return; } QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); if (propElement.isNull()) { if (d->mPrincipalPropertySearchSubJobCount == 0) { emitResult(); } return; } // All requested properties are now under propElement, so let's find them typedef QPair PropertyPair; for (const PropertyPair &fetchProperty : qAsConst(d->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(); d->mResults << result; } } if (d->mPrincipalPropertySearchSubJobCount == 0) { emitResult(); } } QVector< DavPrincipalSearchJob::Result > DavPrincipalSearchJob::results() const { Q_D(const DavPrincipalSearchJob); return d->mResults; } void DavPrincipalSearchJobPrivate::buildReportQuery(QDomDocument &query) const { /* * 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 == DavPrincipalSearchJob::DisplayName) { QDomElement displayName = query.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname")); prop.appendChild(displayName); } else if (mType == DavPrincipalSearchJob::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; for (const PropertyPair &fetchProperty : qAsConst(mFetchProperties)) { QDomElement elem = query.createElementNS(fetchProperty.first, fetchProperty.second); prop.appendChild(elem); } } diff --git a/src/common/davprincipalsearchjob.h b/src/common/davprincipalsearchjob.h index 1d0fa3a..7de483b 100644 --- a/src/common/davprincipalsearchjob.h +++ b/src/common/davprincipalsearchjob.h @@ -1,109 +1,98 @@ /* - Copyright (c) 2011 Grégory Oestreicher + SPDX-FileCopyrightText: 2011 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVPRINCIPALSEARCHJOB_H #define KDAV_DAVPRINCIPALSEARCHJOB_H #include "kdav_export.h" #include "davjobbase.h" #include "davurl.h" #include #include #include #include namespace KDAV { class DavPrincipalSearchJobPrivate; /** * @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 KDAV_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); Q_DECLARE_PRIVATE(DavPrincipalSearchJob) }; } Q_DECLARE_TYPEINFO(KDAV::DavPrincipalSearchJob::Result, Q_MOVABLE_TYPE); #endif diff --git a/src/common/davprotocolbase.cpp b/src/common/davprotocolbase.cpp index 2b4f736..d2e868f 100644 --- a/src/common/davprotocolbase.cpp +++ b/src/common/davprotocolbase.cpp @@ -1,54 +1,43 @@ /* - Copyright (c) 2009 Grégory Oestreicher + SPDX-FileCopyrightText: 2009 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davprotocolbase_p.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_p.h b/src/common/davprotocolbase_p.h index b31df98..9fb73f7 100644 --- a/src/common/davprotocolbase_p.h +++ b/src/common/davprotocolbase_p.h @@ -1,134 +1,123 @@ /* - Copyright (c) 2009 Grégory Oestreicher + SPDX-FileCopyrightText: 2009 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVPROTOCOLBASE_H #define KDAV_DAVPROTOCOLBASE_H #include "kdav_export.h" #include "davcollection.h" #include #include #include #include namespace KDAV { /** * @short Base class for XML query builders */ class 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 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 index 0477497..6f8b7ce 100644 --- a/src/common/davurl.cpp +++ b/src/common/davurl.cpp @@ -1,95 +1,84 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "davurl.h" #include "enums.h" using namespace KDAV; namespace KDAV { class DavUrlPrivate : public QSharedData { public: Protocol mProtocol = KDAV::CalDav; QUrl mUrl; }; } DavUrl::DavUrl() : d(new DavUrlPrivate) { } DavUrl::DavUrl(const QUrl &url, Protocol protocol) : d(new DavUrlPrivate) { d->mUrl = url; d->mProtocol = protocol; } DavUrl::DavUrl(const DavUrl&) = default; DavUrl::~DavUrl() = default; DavUrl& DavUrl::operator=(const DavUrl&) = default; void DavUrl::setUrl(const QUrl &url) { d->mUrl = url; } QUrl DavUrl::url() const { return d->mUrl; } void DavUrl::setProtocol(Protocol protocol) { d->mProtocol = protocol; } Protocol DavUrl::protocol() const { return d->mProtocol; } QString DavUrl::toDisplayString() const { auto url = d->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 index a15bf67..d810eb9 100644 --- a/src/common/davurl.h +++ b/src/common/davurl.h @@ -1,93 +1,82 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVURL_H #define KDAV_DAVURL_H #include "kdav_export.h" #include "enums.h" #include #include #include namespace KDAV { class DavUrlPrivate; /** * @short A helper class to combine url and protocol of a DAV url. */ class KDAV_EXPORT DavUrl { public: /** * Defines a list of DAV url objects. */ typedef QVector List; /** * Creates an empty DAV url. */ DavUrl(); DavUrl(const DavUrl&); ~DavUrl(); DavUrl& operator=(const 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 user-friendly way without login information. */ 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: QSharedDataPointer d; }; KDAV_EXPORT QDataStream &operator<<(QDataStream &out, const DavUrl &url); KDAV_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 index 4da0e9c..f637da6 100644 --- a/src/common/enums.h +++ b/src/common/enums.h @@ -1,54 +1,43 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #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 https://devguide.calconnect.org/CalDAV CardDav, ///< The CardDav protocol as defined in https://devguide.calconnect.org/CardDAV 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 index 923cb4d..ba9e10d 100644 --- a/src/common/etagcache.cpp +++ b/src/common/etagcache.cpp @@ -1,93 +1,82 @@ /* - Copyright (c) 2010 Grégory Oestreicher + SPDX-FileCopyrightText: 2010 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "etagcache.h" #include #include using namespace KDAV; namespace KDAV { class EtagCachePrivate { public: QMap mCache; QSet mChangedRemoteIds; }; } EtagCache::EtagCache(QObject *parent) : QObject(parent) , d(new EtagCachePrivate) { } EtagCache::~EtagCache() = default; void EtagCache::setEtag(const QString &remoteId, const QString &etag) { setEtagInternal(remoteId, etag); if (d->mChangedRemoteIds.contains(remoteId)) { d->mChangedRemoteIds.remove(remoteId); } } void EtagCache::setEtagInternal(const QString &remoteId, const QString &etag) { d->mCache[ remoteId ] = etag; } bool EtagCache::contains(const QString &remoteId) const { return d->mCache.contains(remoteId); } bool EtagCache::etagChanged(const QString &remoteId, const QString &refEtag) const { if (!contains(remoteId)) { return true; } return d->mCache.value(remoteId) != refEtag; } void EtagCache::markAsChanged(const QString &remoteId) { d->mChangedRemoteIds.insert(remoteId); } bool EtagCache::isOutOfDate(const QString &remoteId) const { return d->mChangedRemoteIds.contains(remoteId); } void EtagCache::removeEtag(const QString &remoteId) { d->mChangedRemoteIds.remove(remoteId); d->mCache.remove(remoteId); } QStringList EtagCache::urls() const { return d->mCache.keys(); } QStringList EtagCache::changedRemoteIds() const { return d->mChangedRemoteIds.values(); } diff --git a/src/common/etagcache.h b/src/common/etagcache.h index 7fdb117..3ace77d 100644 --- a/src/common/etagcache.h +++ b/src/common/etagcache.h @@ -1,106 +1,95 @@ /* - Copyright (c) 2010 Grégory Oestreicher + SPDX-FileCopyrightText: 2010 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_ETAGCACHE_H #define KDAV_ETAGCACHE_H #include "kdav_export.h" #include #include #include namespace KDAV { class EtagCachePrivate; /** * @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 KDAV_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); ~EtagCache(); /** * 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: const std::unique_ptr d; }; } #endif diff --git a/src/common/protocolinfo.cpp b/src/common/protocolinfo.cpp index 6dac402..9ba6219 100644 --- a/src/common/protocolinfo.cpp +++ b/src/common/protocolinfo.cpp @@ -1,87 +1,76 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "protocolinfo.h" #include "davmanager_p.h" #include "davprotocolbase_p.h" #include "libkdav_debug.h" using namespace KDAV; bool ProtocolInfo::useMultiget(KDAV::Protocol protocol) { return DavManager::davProtocol(protocol)->useMultiget(); } QString ProtocolInfo::principalHomeSet(KDAV::Protocol protocol) { return DavManager::davProtocol(protocol)->principalHomeSet(); } QString ProtocolInfo::principalHomeSetNS(KDAV::Protocol protocol) { return DavManager::davProtocol(protocol)->principalHomeSetNS(); } QLatin1String ProtocolInfo::protocolName(KDAV::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; } KDAV::Protocol ProtocolInfo::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 ProtocolInfo::contactsMimeType(KDAV::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/protocolinfo.h b/src/common/protocolinfo.h index d84ba7e..d47de50 100644 --- a/src/common/protocolinfo.h +++ b/src/common/protocolinfo.h @@ -1,67 +1,56 @@ /* - Copyright (c) 2019 Volker Krause + SPDX-FileCopyrightText: 2019 Volker Krause - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_PROTOCOLINFO_H #define KDAV_PROTOCOLINFO_H #include "kdav_export.h" #include "enums.h" class QLatin1String; namespace KDAV { /** Information about a DAV protocol. */ namespace ProtocolInfo { /** * Returns whether the @p 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. */ KDAV_EXPORT Q_REQUIRED_RESULT bool useMultiget(KDAV::Protocol protocol); /** Returns the principal home set of @p protocol. */ KDAV_EXPORT Q_REQUIRED_RESULT QString principalHomeSet(KDAV::Protocol protocol); /** Returns the principal home set namespace of @p protocol. */ KDAV_EXPORT Q_REQUIRED_RESULT QString principalHomeSetNS(KDAV::Protocol protocol); /** * Returns the untranslated name of the given DAV @p protocol dialect. */ KDAV_EXPORT Q_REQUIRED_RESULT QLatin1String protocolName(KDAV::Protocol protocol); /** * Returns the protocol matching the given name. This is the opposite of * Utils::protocolName(). */ KDAV_EXPORT Q_REQUIRED_RESULT KDAV::Protocol protocolByName(const QString &name); /** * Returns the mimetype that shall be used for contact DAV resources using @p protocol. */ KDAV_EXPORT Q_REQUIRED_RESULT QString contactsMimeType(KDAV::Protocol protocol); } } #endif // KDAV_PROTOCOLINFO_H diff --git a/src/common/utils.cpp b/src/common/utils.cpp index 2413226..bf3ce8b 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -1,122 +1,111 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "utils_p.h" #include "protocolinfo.h" #include "enums.h" #include "davitem.h" #include "davmanager_p.h" #include "davprotocolbase_p.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; } diff --git a/src/common/utils_p.h b/src/common/utils_p.h index bc42271..3a70074 100644 --- a/src/common/utils_p.h +++ b/src/common/utils_p.h @@ -1,51 +1,40 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_DAVUTILS_P_H #define KDAV_DAVUTILS_P_H #include "enums.h" #include namespace KDAV { namespace Utils { /** * 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 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 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 extractPrivileges(const QDomElement &element); /** * Parses a single tag and returns the final Privileges. */ Q_REQUIRED_RESULT Privileges parsePrivilege(const QDomElement &element); } } #endif diff --git a/src/protocols/caldavprotocol.cpp b/src/protocols/caldavprotocol.cpp index 5615133..1c7a0fe 100644 --- a/src/protocols/caldavprotocol.cpp +++ b/src/protocols/caldavprotocol.cpp @@ -1,421 +1,410 @@ /* - Copyright (c) 2009 Grégory Oestreicher + SPDX-FileCopyrightText: 2009 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "caldavprotocol_p.h" #include "common/utils_p.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_p.h b/src/protocols/caldavprotocol_p.h index d581ea0..0ad22d5 100644 --- a/src/protocols/caldavprotocol_p.h +++ b/src/protocols/caldavprotocol_p.h @@ -1,42 +1,31 @@ /* - Copyright (c) 2009 Grégory Oestreicher + SPDX-FileCopyrightText: 2009 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_CALDAVPROTOCOL_H #define KDAV_CALDAVPROTOCOL_H #include "common/davmultigetprotocol_p.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 index dae3670..31109da 100644 --- a/src/protocols/carddavprotocol.cpp +++ b/src/protocols/carddavprotocol.cpp @@ -1,186 +1,175 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "carddavprotocol_p.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_p.h b/src/protocols/carddavprotocol_p.h index ad07c12..b7983da 100644 --- a/src/protocols/carddavprotocol_p.h +++ b/src/protocols/carddavprotocol_p.h @@ -1,42 +1,31 @@ /* - Copyright (c) 2010 Tobias Koenig + SPDX-FileCopyrightText: 2010 Tobias Koenig - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KDAV_CARDDAVPROTOCOL_H #define KDAV_CARDDAVPROTOCOL_H #include "common/davmultigetprotocol_p.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 index a4c5934..2b73a25 100644 --- a/src/protocols/groupdavprotocol.cpp +++ b/src/protocols/groupdavprotocol.cpp @@ -1,152 +1,141 @@ /* - Copyright (c) 2009 Grégory Oestreicher + SPDX-FileCopyrightText: 2009 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #include "groupdavprotocol_p.h" #include "common/utils_p.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_p.h b/src/protocols/groupdavprotocol_p.h index eee60fd..b33f7d8 100644 --- a/src/protocols/groupdavprotocol_p.h +++ b/src/protocols/groupdavprotocol_p.h @@ -1,37 +1,26 @@ /* - Copyright (c) 2009 Grégory Oestreicher + SPDX-FileCopyrightText: 2009 Grégory Oestreicher - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef GROUPDAVPROTOCOL_H #define GROUPDAVPROTOCOL_H #include "common/davprotocolbase_p.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/testserver.cpp b/test/testserver.cpp index fc4533c..62d893c 100644 --- a/test/testserver.cpp +++ b/test/testserver.cpp @@ -1,162 +1,151 @@ /* - Copyright (c) 2016 Sandro Knauß + SPDX-FileCopyrightText: 2016 Sandro Knauß - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library 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 Library General Public - License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.0-or-later */ #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(); const auto collections = job->collections(); for (const auto &collection : 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(); const auto changedItems = itemListJob->changedItems(); for (const auto &item : 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() != QLatin1String("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(); } } }