diff --git a/AndroidManifest.xml b/AndroidManifest.xml new file mode 100644 index 00000000..dfa974cc --- /dev/null +++ b/AndroidManifest.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..5bb952bd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,58 @@ + +DAVdroid is free and open-source software, licensed under the [GPLv3 License](COPYING). +If you like our project, please contribute to it. + +# How to contribute + +## Reporting issues + +An issue might be a bug, an enhancement request or something in between. If you think you +have found a bug or if you want to request some enhancement, please: + +1. Read the [Configuration](http://davdroid.bitfire.at/configuration) and [FAQ](http://davdroid.bitfire.at/faq/) + pages carefully. The most common issues/usage challenges are explained there. +2. Search the Web for the problem, maybe ask competent friends or in forums. +3. Browse through the [open issues](https://github.com/rfc2822/davdroid/issues). You can + also search the issues in the search field on top of the page. Please have a look + into the closed issues, too, because many requests have already been handled (and can't/won't + be fixed, for instance). +4. **[Fetch verbose logs](https://github.com/rfc2822/davdroid/wiki/How-to-view-the-logs) and prepare + them. Remove `Authorization: Basic xxxxxx` headers and other private data.** Extracting the + logs may be cumbersome work in the first time, but it's absolutely necessary in order to + handle your issue. +5. [Create a new issue](https://github.com/rfc2822/davdroid/issues/new), containing + * a useful summary of the problem ("Crash when syncing contacts with large photos" instead of "CRASH PLEASE HELP"), + * your DAVdroid version and source ("DAVdroid 0.5.10 from F-Droid"), + * your Android version and device model ("Samsung Galaxy S2 running Android 4.4.2 (CyanogenMod 11-20140504-SNAPSHOT-M6-i9100)"), + * your CalDAV/CardDAV server software, version and hosting information ("OwnCloud 6, hosted on virtual server"), + * a problem description, including **instructions on how to reproduce the problem** (we need to + reproduce the problem before we can fix it!), + * **verbose logs including the network traffic** (see step before). Enquote the logs with three backticks ``` + before and after, or post them onto http://gist.github.com and provide a link. + + +## Pull requests + +We're very happy about pull requests for + +* source code, +* documentation, +* translation (strings). + +However, if you want to contribute source code, please talk with us in the +corresponding issue before because will only merge pull requests that + +* match our product goals, +* have the necessary code quality, +* don't interfere with other near-term future development. + +However, feel free to fork the repository and do your changes anyway +(that's why it's open-source). Just don't expect your strategic changes to be +merged if there's no consensus in the issue before. + + +## Donations + +If you want to support this project, please also consider [donating to DAVdroid](http://davdroid.bitfire.at/donate) +or [purchasing it in one of the commercial stores](http://davdroid.bitfire.at/download). + diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU 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. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 00000000..41c9c8f2 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ + +DAVDROID +======== + +Please see the [DAVdroid Web site](https://davdroid.bitfire.at) for +detailled information about DAVdroid. + +DAVdroid is licensed under the [GPLv3 License](COPYING). + +Twitter: [@davdroidapp](https://twitter.com/davdroidapp) + + +USED THIRD-PARTY LIBRARIES +========================== + +* [Apache HttpClient](http://hc.apache.org) ([httpclientandroidlib](https://code.google.com/p/httpclientandroidlib/) flavour) – [Apache License](http://www.apache.org/licenses/) +* [iCal4j](http://ical4j.sourceforge.net/) – [New BSD License](http://sourceforge.net/p/ical4j/ical4j/ci/default/tree/LICENSE) +* [ez-vcard](https://code.google.com/p/ez-vcard/) – [New BSD License](http://opensource.org/licenses/BSD-3-Clause) +* [Simple XML Serialization](http://simple.sourceforge.net/) – [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) +* [Project Lombok](http://projectlombok.org/) – [MIT License](http://opensource.org/licenses/mit-license.php) +* [dnsjava](http://www.xbill.org/dnsjava/) – [BSD license](http://www.xbill.org/dnsjava/dnsjava-current/LICENSE) diff --git a/build.xml b/build.xml new file mode 100644 index 00000000..4a6bb871 --- /dev/null +++ b/build.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Creating library output jar file... + + + + + + + Custom jar packaging exclusion: ${android.package.excludes} + + + + + + + + + + + + + + + + + + diff --git a/compile-libs/lombok.jar b/compile-libs/lombok.jar new file mode 100644 index 00000000..4141433c Binary files /dev/null and b/compile-libs/lombok.jar differ diff --git a/doc/NIST.SP.800-52r1.pdf b/doc/NIST.SP.800-52r1.pdf new file mode 100644 index 00000000..0436449b Binary files /dev/null and b/doc/NIST.SP.800-52r1.pdf differ diff --git a/doc/how_davdroid_works.svgz b/doc/how_davdroid_works.svgz new file mode 100644 index 00000000..93b51e46 Binary files /dev/null and b/doc/how_davdroid_works.svgz differ diff --git a/doc/rfc3744-webdav-access-control-protocol.txt b/doc/rfc3744-webdav-access-control-protocol.txt new file mode 100644 index 00000000..afcdb2a3 --- /dev/null +++ b/doc/rfc3744-webdav-access-control-protocol.txt @@ -0,0 +1,4035 @@ + + + + + + +Network Working Group G. Clemm +Request for Comments: 3744 IBM +Category: Standards Track J. Reschke + greenbytes + E. Sedlar + Oracle Corporation + J. Whitehead + U.C. Santa Cruz + May 2004 + + + Web Distributed Authoring and Versioning (WebDAV) + Access Control Protocol + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2004). All Rights Reserved. + +Abstract + + This document specifies a set of methods, headers, message bodies, + properties, and reports that define Access Control extensions to the + WebDAV Distributed Authoring Protocol. This protocol permits a + client to read and modify access control lists that instruct a server + whether to allow or deny operations upon a resource (such as + HyperText Transfer Protocol (HTTP) method invocations) by a given + principal. A lightweight representation of principals as Web + resources supports integration of a wide range of user management + repositories. Search operations allow discovery and manipulation of + principals using human names. + + + + + + + + + + + + + +Clemm, et al. Standards Track [Page 1] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 4 + 1.1. Terms. . . . . . . . . . . . . . . . . . . . . . . . . . 6 + 1.2. Notational Conventions . . . . . . . . . . . . . . . . . 8 + 2. Principals . . . . . . . . . . . . . . . . . . . . . . . . . . 8 + 3. Privileges . . . . . . . . . . . . . . . . . . . . . . . . . . 8 + 3.1. DAV:read Privilege . . . . . . . . . . . . . . . . . . . 10 + 3.2. DAV:write Privilege. . . . . . . . . . . . . . . . . . . 10 + 3.3. DAV:write-properties Privilege . . . . . . . . . . . . . 10 + 3.4. DAV:write-content Privilege. . . . . . . . . . . . . . . 11 + 3.5. DAV:unlock Privilege . . . . . . . . . . . . . . . . . . 11 + 3.6. DAV:read-acl Privilege . . . . . . . . . . . . . . . . . 11 + 3.7. DAV:read-current-user-privilege-set Privilege. . . . . . 12 + 3.8. DAV:write-acl Privilege. . . . . . . . . . . . . . . . . 12 + 3.9. DAV:bind Privilege . . . . . . . . . . . . . . . . . . . 12 + 3.10. DAV:unbind Privilege . . . . . . . . . . . . . . . . . . 12 + 3.11. DAV:all Privilege. . . . . . . . . . . . . . . . . . . . 13 + 3.12. Aggregation of Predefined Privileges . . . . . . . . . . 13 + 4. Principal Properties . . . . . . . . . . . . . . . . . . . . . 13 + 4.1. DAV:alternate-URI-set. . . . . . . . . . . . . . . . . . 14 + 4.2. DAV:principal-URL. . . . . . . . . . . . . . . . . . . . 14 + 4.3. DAV:group-member-set . . . . . . . . . . . . . . . . . . 14 + 4.4. DAV:group-membership . . . . . . . . . . . . . . . . . . 14 + 5. Access Control Properties. . . . . . . . . . . . . . . . . . . 15 + 5.1. DAV:owner. . . . . . . . . . . . . . . . . . . . . . . . 15 + 5.1.1. Example: Retrieving DAV:owner . . . . . . . . . . 15 + 5.1.2. Example: An Attempt to Set DAV:owner. . . . . . . 16 + 5.2. DAV:group. . . . . . . . . . . . . . . . . . . . . . . . 18 + 5.3. DAV:supported-privilege-set. . . . . . . . . . . . . . . 18 + 5.3.1. Example: Retrieving a List of Privileges + Supported on a Resource . . . . . . . . . . . . . 19 + 5.4. DAV:current-user-privilege-set . . . . . . . . . . . . . 21 + 5.4.1. Example: Retrieving the User's Current Set of + Assigned Privileges . . . . . . . . . . . . . . . 22 + 5.5. DAV:acl. . . . . . . . . . . . . . . . . . . . . . . . . 23 + 5.5.1. ACE Principal . . . . . . . . . . . . . . . . . . 23 + 5.5.2. ACE Grant and Deny. . . . . . . . . . . . . . . . 25 + 5.5.3. ACE Protection. . . . . . . . . . . . . . . . . . 25 + 5.5.4. ACE Inheritance . . . . . . . . . . . . . . . . . 25 + 5.5.5. Example: Retrieving a Resource's Access Control + List. . . . . . . . . . . . . . . . . . . . . . . 25 + 5.6. DAV:acl-restrictions . . . . . . . . . . . . . . . . . . 27 + 5.6.1. DAV:grant-only. . . . . . . . . . . . . . . . . . 27 + 5.6.2. DAV:no-invert ACE Constraint. . . . . . . . . . . 28 + 5.6.3. DAV:deny-before-grant . . . . . . . . . . . . . . 28 + 5.6.4. Required Principals . . . . . . . . . . . . . . . 28 + 5.6.5. Example: Retrieving DAV:acl-restrictions. . . . . 28 + + + +Clemm, et al. Standards Track [Page 2] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + 5.7. DAV:inherited-acl-set. . . . . . . . . . . . . . . . . . 29 + 5.8. DAV:principal-collection-set . . . . . . . . . . . . . . 30 + 5.8.1. Example: Retrieving DAV:principal-collection-set. 30 + 5.9. Example: PROPFIND to retrieve access control properties. 32 + 6. ACL Evaluation . . . . . . . . . . . . . . . . . . . . . . . . 36 + 7. Access Control and existing methods. . . . . . . . . . . . . . 37 + 7.1. Any HTTP method. . . . . . . . . . . . . . . . . . . . . 37 + 7.1.1. Error Handling. . . . . . . . . . . . . . . . . . 37 + 7.2. OPTIONS. . . . . . . . . . . . . . . . . . . . . . . . . 38 + 7.2.1. Example - OPTIONS . . . . . . . . . . . . . . . . 39 + 7.3. MOVE . . . . . . . . . . . . . . . . . . . . . . . . . . 39 + 7.4. COPY . . . . . . . . . . . . . . . . . . . . . . . . . . 39 + 7.5. LOCK . . . . . . . . . . . . . . . . . . . . . . . . . . 39 + 8. Access Control Methods . . . . . . . . . . . . . . . . . . . . 40 + 8.1. ACL. . . . . . . . . . . . . . . . . . . . . . . . . . . 40 + 8.1.1. ACL Preconditions . . . . . . . . . . . . . . . . 40 + 8.1.2. Example: the ACL method . . . . . . . . . . . . . 42 + 8.1.3. Example: ACL method failure due to protected + ACE conflict. . . . . . . . . . . . . . . . . . . 43 + 8.1.4. Example: ACL method failure due to an + inherited ACE conflict. . . . . . . . . . . . . . 44 + 8.1.5. Example: ACL method failure due to an attempt + to set grant and deny in a single ACE . . . . . . 45 + 9. Access Control Reports . . . . . . . . . . . . . . . . . . . . 46 + 9.1. REPORT Method. . . . . . . . . . . . . . . . . . . . . . 46 + 9.2. DAV:acl-principal-prop-set Report. . . . . . . . . . . . 47 + 9.2.1. Example: DAV:acl-principal-prop-set Report. . . . 48 + 9.3. DAV:principal-match REPORT . . . . . . . . . . . . . . . 49 + 9.3.1. Example: DAV:principal-match REPORT . . . . . . . 50 + 9.4. DAV:principal-property-search REPORT . . . . . . . . . . 51 + 9.4.1. Matching. . . . . . . . . . . . . . . . . . . . . 53 + 9.4.2. Example: successful DAV:principal-property-search + REPORT. . . . . . . . . . . . . . . . . . . . . . 54 + 9.5. DAV:principal-search-property-set REPORT . . . . . . . . 56 + 9.5.1. Example: DAV:principal-search-property-set + REPORT. . . . . . . . . . . . . . . . . . . . . . 58 + 10. XML Processing . . . . . . . . . . . . . . . . . . . . . . . . 59 + 11. Internationalization Considerations. . . . . . . . . . . . . . 59 + 12. Security Considerations. . . . . . . . . . . . . . . . . . . . 60 + 12.1. Increased Risk of Compromised Users. . . . . . . . . . . 60 + 12.2. Risks of the DAV:read-acl and + DAV:current-user-privilege-set Privileges. . . . . . . . 60 + 12.3. No Foreknowledge of Initial ACL. . . . . . . . . . . . . 61 + 13. Authentication . . . . . . . . . . . . . . . . . . . . . . . . 61 + 14. IANA Considerations. . . . . . . . . . . . . . . . . . . . . . 62 + 15. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 62 + + + + + +Clemm, et al. Standards Track [Page 3] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + 16. References . . . . . . . . . . . . . . . . . . . . . . . . . . 62 + 16.1. Normative References . . . . . . . . . . . . . . . . . . 62 + 16.2. Informative References . . . . . . . . . . . . . . . . . 63 + Appendices + A. WebDAV XML Document Type Definition Addendum . . . . . . . . . 64 + B. WebDAV Method Privilege Table (Normative). . . . . . . . . . . 67 + Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 + Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 71 + Full Copyright Statement. . . . . . . . . . . . . . . . . . . . . 72 + +1. Introduction + + The goal of the WebDAV access control extensions is to provide an + interoperable mechanism for handling discretionary access control for + content and metadata managed by WebDAV servers. WebDAV access + control can be implemented on content repositories with security as + simple as that of a UNIX file system, as well as more sophisticated + models. The underlying principle of access control is that who you + are determines what operations you can perform on a resource. The + "who you are" is defined by a "principal" identifier; users, client + software, servers, and groups of the previous have principal + identifiers. The "operations you can perform" are determined by a + single "access control list" (ACL) associated with a resource. An + ACL contains a set of "access control entries" (ACEs), where each ACE + specifies a principal and a set of privileges that are either granted + or denied to that principal. When a principal submits an operation + (such as an HTTP or WebDAV method) to a resource for execution, the + server evaluates the ACEs in the ACL to determine if the principal + has permission for that operation. + + Since every ACE contains the identifier of a principal, client + software operated by a human must provide a mechanism for selecting + this principal. This specification uses http(s) scheme URLs to + identify principals, which are represented as WebDAV-capable + resources. There is no guarantee that the URLs identifying + principals will be meaningful to a human. For example, + http://www.example.com/u/256432 and + http://www.example.com/people/Greg.Stein are both valid URLs that + could be used to identify the same principal. To remedy this, every + principal resource has the DAV:displayname property containing a + human-readable name for the principal. + + Since a principal can be identified by multiple URLs, it raises the + problem of determining exactly which principal is being referenced in + a given ACE. It is impossible for a client to determine that an ACE + granting the read privilege to http://www.example.com/people/ + Greg.Stein also affects the principal at http://www.example.com/u/ + 256432. That is, a client has no mechanism for determining that two + + + +Clemm, et al. Standards Track [Page 4] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + URLs identify the same principal resource. As a result, this + specification requires clients to use just one of the many possible + URLs for a principal when creating ACEs. A client can discover which + URL to use by retrieving the DAV:principal-URL property (Section 4.2) + from a principal resource. No matter which of the principal's URLs + is used with PROPFIND, the property always returns the same URL. + + With a system having hundreds to thousands of principals, the problem + arises of how to allow a human operator of client software to select + just one of these principals. One approach is to use broad + collection hierarchies to spread the principals over a large number + of collections, yielding few principals per collection. An example + of this is a two level hierarchy with the first level containing 36 + collections (a-z, 0-9), and the second level being another 36, + creating collections /a/a/, /a/b/, ..., /a/z/, such that a principal + with last name "Stein" would appear at /s/t/Stein. In effect, this + pre-computes a common query, search on last name, and encodes it into + a hierarchy. The drawback with this scheme is that it handles only a + small set of predefined queries, and drilling down through the + collection hierarchy adds unnecessary steps (navigate down/up) when + the user already knows the principal's name. While organizing + principal URLs into a hierarchy is a valid namespace organization, + users should not be forced to navigate this hierarchy to select a + principal. + + This specification provides the capability to perform substring + searches over a small set of properties on the resources representing + principals. This permits searches based on last name, first name, + user name, job title, etc. Two separate searches are supported, both + via the REPORT method, one to search principal resources + (DAV:principal-property-search, Section 9.4), the other to determine + which properties may be searched at all (DAV:principal-search- + property-set, Section 9.5). + + Once a principal has been identified in an ACE, a server evaluating + that ACE must know the identity of the principal making a protocol + request, and must validate that that principal is who they claim to + be, a process known as authentication. This specification + intentionally omits discussion of authentication, as the HTTP + protocol already has a number of authentication mechanisms [RFC2617]. + Some authentication mechanism (such as HTTP Digest Authentication, + which all WebDAV compliant implementations are required to support) + must be available to validate the identity of a principal. + + + + + + + + +Clemm, et al. Standards Track [Page 5] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + The following issues are out of scope for this document: + + o Access control that applies only to a particular property on a + resource (excepting the access control properties DAV:acl and + DAV:current-user-privilege-set), rather than the entire resource, + + o Role-based security (where a role can be seen as a dynamically + defined group of principals), + + o Specification of the ways an ACL on a resource is initialized, + + o Specification of an ACL that applies globally to all resources, + rather than to a particular resource. + + o Creation and maintenance of resources representing people or + computational agents (principals), and groups of these. + + This specification is organized as follows. Section 1.1 defines key + concepts used throughout the specification, and is followed by a more + in-depth discussion of principals (Section 2), and privileges + (Section 3). Properties defined on principals are specified in + Section 4, and access control properties for content resources are + specified in Section 5. The ways ACLs are to be evaluated is + described in Section 6. Client discovery of access control + capability using OPTIONS is described in Section 7.2. Interactions + between access control functionality and existing HTTP and WebDAV + methods are described in the remainder of Section 7. The access + control setting method, ACL, is specified in Section 8. Four reports + that provide limited server-side searching capabilities are described + in Section 9. Sections on XML processing (Section 10), + Internationalization considerations (Section 11), security + considerations (Section 12), and authentication (Section 13) round + out the specification. An appendix (Appendix A) provides an XML + Document Type Definition (DTD) for the XML elements defined in the + specification. + +1.1. Terms + + This document uses the terms defined in HTTP [RFC2616] and WebDAV + [RFC2518]. In addition, the following terms are defined: + + principal + + A "principal" is a distinct human or computational actor that + initiates access to network resources. In this protocol, a + principal is an HTTP resource that represents such an actor. + + + + + +Clemm, et al. Standards Track [Page 6] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + group + + A "group" is a principal that represents a set of other + principals. + + privilege + + A "privilege" controls access to a particular set of HTTP + operations on a resource. + + aggregate privilege + + An "aggregate privilege" is a privilege that contains a set of + other privileges. + + abstract privilege + + The modifier "abstract", when applied to a privilege on a + resource, means the privilege cannot be set in an access control + element (ACE) on that resource. + + access control list (ACL) + + An "ACL" is a list of access control elements that define access + control to a particular resource. + + access control element (ACE) + + An "ACE" either grants or denies a particular set of (non- + abstract) privileges for a particular principal. + + inherited ACE + + An "inherited ACE" is an ACE that is dynamically shared from the + ACL of another resource. When a shared ACE changes on the primary + resource, it is also changed on inheriting resources. + + protected property + + A "protected property" is one whose value cannot be updated except + by a method explicitly defined as updating that specific property. + In particular, a protected property cannot be updated with a + PROPPATCH request. + + + + + + + + +Clemm, et al. Standards Track [Page 7] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +1.2. Notational Conventions + + The augmented BNF used by this document to describe protocol elements + is described in Section 2.1 of [RFC2616]. Because this augmented BNF + uses the basic production rules provided in Section 2.2 of [RFC2616], + those rules apply to this document as well. + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + Definitions of XML elements in this document use XML element type + declarations (as found in XML Document Type Declarations), described + in Section 3.2 of [REC-XML]. When an XML element type in the "DAV:" + namespace is referenced in this document outside of the context of an + XML fragment, the string "DAV:" will be prefixed to the element name. + +2. Principals + + A principal is a network resource that represents a distinct human or + computational actor that initiates access to network resources. + Users and groups are represented as principals in many + implementations; other types of principals are also possible. A URI + of any scheme MAY be used to identify a principal resource. However, + servers implementing this specification MUST expose principal + resources at an http(s) URL, which is a privileged scheme that points + to resources that have additional properties, as described in Section + 4. So, a principal resource can have multiple URIs, one of which has + to be an http(s) scheme URL. Although an implementation SHOULD + support PROPFIND and MAY support PROPPATCH to access and modify + information about a principal, it is not required to do so. + + A principal resource may be a group, where a group is a principal + that represents a set of other principals, called the members of the + group. If a person or computational agent matches a principal + resource that is a member of a group, they also match the group. + Membership in a group is recursive, so if a principal is a member of + group GRPA, and GRPA is a member of group GRPB, then the principal is + also a member of GRPB. + +3. Privileges + + Ability to perform a given method on a resource MUST be controlled by + one or more privileges. Authors of protocol extensions that define + new HTTP methods SHOULD specify which privileges (by defining new + privileges, or mapping to ones below) are required to perform the + method. A principal with no privileges to a resource MUST be denied + any HTTP access to that resource, unless the principal matches an ACE + + + +Clemm, et al. Standards Track [Page 8] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + constructed using the DAV:all, DAV:authenticated, or + DAV:unauthenticated pseudo-principals (see Section 5.5.1). Servers + MUST report a 403 "Forbidden" error if access is denied, except in + the case where the privilege restricts the ability to know the + resource exists, in which case 404 "Not Found" may be returned. + + Privileges may be containers of other privileges, in which case they + are termed "aggregate privileges". If a principal is granted or + denied an aggregate privilege, it is semantically equivalent to + granting or denying each of the aggregated privileges individually. + For example, an implementation may define add-member and remove- + member privileges that control the ability to add and remove a member + of a group. Since these privileges control the ability to update the + state of a group, these privileges would be aggregated by the + DAV:write privilege on a group, and granting the DAV:write privilege + on a group would also grant the add-member and remove-member + privileges. + + Privileges may be declared to be "abstract" for a given resource, in + which case they cannot be set in an ACE on that resource. Aggregate + and non-aggregate privileges are both capable of being abstract. + Abstract privileges are useful for modeling privileges that otherwise + would not be exposed via the protocol. Abstract privileges also + provide server implementations with flexibility in implementing the + privileges defined in this specification. For example, if a server + is incapable of separating the read resource capability from the read + ACL capability, it can still model the DAV:read and DAV:read-acl + privileges defined in this specification by declaring them abstract, + and containing them within a non-abstract aggregate privilege (say, + read-all) that holds DAV:read, and DAV:read-acl. In this way, it is + possible to set the aggregate privilege, read-all, thus coupling the + setting of DAV:read and DAV:read-acl, but it is not possible to set + DAV:read, or DAV:read-acl individually. Since aggregate privileges + can be abstract, it is also possible to use abstract privileges to + group or organize non-abstract privileges. Privilege containment + loops are not allowed; therefore, a privilege MUST NOT contain + itself. For example, DAV:read cannot contain DAV:read. + + The set of privileges that apply to a particular resource may vary + with the DAV:resourcetype of the resource, as well as between + different server implementations. To promote interoperability, + however, this specification defines a set of well-known privileges + (e.g., DAV:read, DAV:write, DAV:read-acl, DAV:write-acl, DAV:read- + current-user-privilege-set, and DAV:all), which can at least be used + to classify the other privileges defined on a particular resource. + The access permissions on null resources (defined in [RFC2518], + Section 3) are solely those they inherit (if any), and they are not + discoverable (i.e., the access control properties specified in + + + +Clemm, et al. Standards Track [Page 9] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + Section 5 are not defined on null resources). On the transition from + null to stateful resource, the initial access control list is set by + the server's default ACL value policy (if any). + + Server implementations MAY define new privileges beyond those defined + in this specification. Privileges defined by individual + implementations MUST NOT use the DAV: namespace, and instead should + use a namespace that they control, such as an http scheme URL. + +3.1. DAV:read Privilege + + The read privilege controls methods that return information about the + state of the resource, including the resource's properties. Affected + methods include GET and PROPFIND. Any implementation-defined + privilege that also controls access to GET and PROPFIND must be + aggregated under DAV:read - if an ACL grants access to DAV:read, the + client may expect that no other privilege needs to be granted to have + access to GET and PROPFIND. Additionally, the read privilege MUST + control the OPTIONS method. + + + +3.2. DAV:write Privilege + + The write privilege controls methods that lock a resource or modify + the content, dead properties, or (in the case of a collection) + membership of the resource, such as PUT and PROPPATCH. Note that + state modification is also controlled via locking (see section 5.3 of + [RFC2518]), so effective write access requires that both write + privileges and write locking requirements are satisfied. Any + implementation-defined privilege that also controls access to methods + modifying content, dead properties or collection membership must be + aggregated under DAV:write, e.g., if an ACL grants access to + DAV:write, the client may expect that no other privilege needs to be + granted to have access to PUT and PROPPATCH. + + + +3.3. DAV:write-properties Privilege + + The DAV:write-properties privilege controls methods that modify the + dead properties of the resource, such as PROPPATCH. Whether this + privilege may be used to control access to any live properties is + determined by the implementation. Any implementation-defined + privilege that also controls access to methods modifying dead + properties must be aggregated under DAV:write-properties - e.g., if + + + + + +Clemm, et al. Standards Track [Page 10] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + an ACL grants access to DAV:write-properties, the client can safely + expect that no other privilege needs to be granted to have access to + PROPPATCH. + + + +3.4. DAV:write-content Privilege + + The DAV:write-content privilege controls methods that modify the + content of an existing resource, such as PUT. Any implementation- + defined privilege that also controls access to content must be + aggregated under DAV:write-content - e.g., if an ACL grants access to + DAV:write-content, the client can safely expect that no other + privilege needs to be granted to have access to PUT. Note that PUT - + when applied to an unmapped URI - creates a new resource and + therefore is controlled by the DAV:bind privilege on the parent + collection. + + + +3.5. DAV:unlock Privilege + + The DAV:unlock privilege controls the use of the UNLOCK method by a + principal other than the lock owner (the principal that created a + lock can always perform an UNLOCK). While the set of users who may + lock a resource is most commonly the same set of users who may modify + a resource, servers may allow various kinds of administrators to + unlock resources locked by others. Any privilege controlling access + by non-lock owners to UNLOCK MUST be aggregated under DAV:unlock. + + A lock owner can always remove a lock by issuing an UNLOCK with the + correct lock token and authentication credentials. That is, even if + a principal does not have DAV:unlock privilege, they can still remove + locks they own. Principals other than the lock owner can remove a + lock only if they have DAV:unlock privilege and they issue an UNLOCK + with the correct lock token. Lock timeout is not affected by the + DAV:unlock privilege. + + + +3.6. DAV:read-acl Privilege + + The DAV:read-acl privilege controls the use of PROPFIND to retrieve + the DAV:acl property of the resource. + + + + + + + +Clemm, et al. Standards Track [Page 11] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +3.7. DAV:read-current-user-privilege-set Privilege + + The DAV:read-current-user-privilege-set privilege controls the use of + PROPFIND to retrieve the DAV:current-user-privilege-set property of + the resource. + + Clients are intended to use this property to visually indicate in + their UI items that are dependent on the permissions of a resource, + for example, by graying out resources that are not writable. + + This privilege is separate from DAV:read-acl because there is a need + to allow most users access to the privileges permitted the current + user (due to its use in creating the UI), while the full ACL contains + information that may not be appropriate for the current authenticated + user. As a result, the set of users who can view the full ACL is + expected to be much smaller than those who can read the current user + privilege set, and hence distinct privileges are needed for each. + + + +3.8. DAV:write-acl Privilege + + The DAV:write-acl privilege controls use of the ACL method to modify + the DAV:acl property of the resource. + + + +3.9. DAV:bind Privilege + + The DAV:bind privilege allows a method to add a new member URL to the + specified collection (for example via PUT or MKCOL). It is ignored + for resources that are not collections. + + + +3.10. DAV:unbind Privilege + + The DAV:unbind privilege allows a method to remove a member URL from + the specified collection (for example via DELETE or MOVE). It is + ignored for resources that are not collections. + + + + + + + + + + + +Clemm, et al. Standards Track [Page 12] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +3.11. DAV:all Privilege + + DAV:all is an aggregate privilege that contains the entire set of + privileges that can be applied to the resource. + + + +3.12. Aggregation of Predefined Privileges + + Server implementations are free to aggregate the predefined + privileges (defined above in Sections 3.1-3.10) subject to the + following limitations: + + DAV:read-acl MUST NOT contain DAV:read, DAV:write, DAV:write-acl, + DAV:write-properties, DAV:write-content, or DAV:read-current-user- + privilege-set. + + DAV:write-acl MUST NOT contain DAV:write, DAV:read, DAV:read-acl, or + DAV:read-current-user-privilege-set. + + DAV:read-current-user-privilege-set MUST NOT contain DAV:write, + DAV:read, DAV:read-acl, or DAV:write-acl. + + DAV:write MUST NOT contain DAV:read, DAV:read-acl, or DAV:read- + current-user-privilege-set. + + DAV:read MUST NOT contain DAV:write, DAV:write-acl, DAV:write- + properties, or DAV:write-content. + + DAV:write MUST contain DAV:bind, DAV:unbind, DAV:write-properties and + DAV:write-content. + +4. Principal Properties + + Principals are manifested to clients as a WebDAV resource, identified + by a URL. A principal MUST have a non-empty DAV:displayname property + (defined in Section 13.2 of [RFC2518]), and a DAV:resourcetype + property (defined in Section 13.9 of [RFC2518]). Additionally, a + principal MUST report the DAV:principal XML element in the value of + the DAV:resourcetype property. The element type declaration for + DAV:principal is: + + + + + + + + + + +Clemm, et al. Standards Track [Page 13] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + This protocol defines the following additional properties for a + principal. Since it can be expensive for a server to retrieve access + control information, the name and value of these properties SHOULD + NOT be returned by a PROPFIND allprop request (as defined in Section + 12.14.1 of [RFC2518]). + +4.1. DAV:alternate-URI-set + + This protected property, if non-empty, contains the URIs of network + resources with additional descriptive information about the + principal. This property identifies additional network resources + (i.e., it contains one or more URIs) that may be consulted by a + client to gain additional knowledge concerning a principal. One + expected use for this property is the storage of an LDAP [RFC2255] + scheme URL. A user-agent encountering an LDAP URL could use LDAP + [RFC2251] to retrieve additional machine-readable directory + information about the principal, and display that information in its + user interface. Support for this property is REQUIRED, and the value + is empty if no alternate URI exists for the principal. + + + +4.2. DAV:principal-URL + + A principal may have many URLs, but there must be one "principal URL" + that clients can use to uniquely identify a principal. This + protected property contains the URL that MUST be used to identify + this principal in an ACL request. Support for this property is + REQUIRED. + + + +4.3. DAV:group-member-set + + This property of a group principal identifies the principals that are + direct members of this group. Since a group may be a member of + another group, a group may also have indirect members (i.e., the + members of its direct members). A URL in the DAV:group-member-set + for a principal MUST be the DAV:principal-URL of that principal. + + + +4.4. DAV:group-membership + + This protected property identifies the groups in which the principal + is directly a member. Note that a server may allow a group to be a + member of another group, in which case the DAV:group-membership of + + + + +Clemm, et al. Standards Track [Page 14] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + those other groups would need to be queried in order to determine the + groups in which the principal is indirectly a member. Support for + this property is REQUIRED. + + + +5. Access Control Properties + + This specification defines a number of new properties for WebDAV + resources. Access control properties may be retrieved just like + other WebDAV properties, using the PROPFIND method. Since it is + expensive, for many servers, to retrieve access control information, + a PROPFIND allprop request (as defined in Section 12.14.1 of + [RFC2518]) SHOULD NOT return the names and values of the properties + defined in this section. + + Access control properties (especially DAV:acl and DAV:inherited-acl- + set) are defined on the resource identified by the Request-URI of a + PROPFIND request. A direct consequence is that if the resource is + accessible via multiple URI, the value of access control properties + is the same across these URI. + + HTTP resources that support the WebDAV Access Control Protocol MUST + contain the following properties. Null resources (described in + Section 3 of [RFC2518]) MUST NOT contain the following properties. + +5.1. DAV:owner + + This property identifies a particular principal as being the "owner" + of the resource. Since the owner of a resource often has special + access control capabilities (e.g., the owner frequently has permanent + DAV:write-acl privilege), clients might display the resource owner in + their user interface. + + Servers MAY implement DAV:owner as protected property and MAY return + an empty DAV:owner element as property value in case no owner + information is available. + + + +5.1.1. Example: Retrieving DAV:owner + + This example shows a client request for the value of the DAV:owner + property from a collection resource with URL http://www.example.com/ + papers/. The principal making the request is authenticated using + Digest authentication. The value of DAV:owner is the URL http:// + www.example.com/acl/users/gstein, wrapped in the DAV:href XML + element. + + + +Clemm, et al. Standards Track [Page 15] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + >> Request << + + PROPFIND /papers/ HTTP/1.1 + Host: www.example.com + Content-type: text/xml; charset="utf-8" + Content-Length: xxx + Depth: 0 + Authorization: Digest username="jim", + realm="users@example.com", nonce="...", + uri="/papers/", response="...", opaque="..." + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + + + + + http://www.example.com/papers/ + + + + http://www.example.com/acl/users/gstein + + + HTTP/1.1 200 OK + + + + +5.1.2. Example: An Attempt to Set DAV:owner + + The following example shows a client request to modify the value of + the DAV:owner property on the resource with URL . Since DAV:owner is a protected property on + this particular server, it responds with a 207 (Multi-Status) + response that contains a 403 (Forbidden) status code for the act of + setting DAV:owner. Section 8.2.1 of [RFC2518] describes PROPPATCH + status code information, Section 11 of [RFC2518] describes the + + + +Clemm, et al. Standards Track [Page 16] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + Multi-Status response and Sections 1.6 and 3.12 of [RFC3253] describe + additional error marshaling for PROPPATCH attempts on protected + properties. + + >> Request << + + PROPPATCH /papers/ HTTP/1.1 + Host: www.example.com + Content-type: text/xml; charset="utf-8" + Content-Length: xxx + Depth: 0 + Authorization: Digest username="jim", + realm="users@example.com", nonce="...", + uri="/papers/", response="...", opaque="..." + + + + + + + http://www.example.com/acl/users/jim + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + + + + + http://www.example.com/papers/ + + + HTTP/1.1 403 Forbidden + + + Failure to set protected property (DAV:owner) + + + + + + + + + +Clemm, et al. Standards Track [Page 17] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +5.2. DAV:group + + This property identifies a particular principal as being the "group" + of the resource. This property is commonly found on repositories + that implement the Unix privileges model. + + Servers MAY implement DAV:group as protected property and MAY return + an empty DAV:group element as property value in case no group + information is available. + + + +5.3. DAV:supported-privilege-set + + This is a protected property that identifies the privileges defined + for the resource. + + + + Each privilege appears as an XML element, where aggregate privileges + list as sub-elements all of the privileges that they aggregate. + + + + + An abstract privilege MUST NOT be used in an ACE for that resource. + Servers MUST fail an attempt to set an abstract privilege. + + + + A description is a human-readable description of what this privilege + controls access to. Servers MUST indicate the human language of the + description using the xml:lang attribute and SHOULD consider the HTTP + Accept-Language request header when selecting one of multiple + available languages. + + + + It is envisioned that a WebDAV ACL-aware administrative client would + list the supported privileges in a dialog box, and allow the user to + choose non-abstract privileges to apply in an ACE. The privileges + tree is useful programmatically to map well-known privileges (defined + by WebDAV or other standards groups) into privileges that are + supported by any particular server implementation. The privilege + tree also serves to hide complexity in implementations allowing large + number of privileges to be defined by displaying aggregates to the + user. + + + +Clemm, et al. Standards Track [Page 18] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +5.3.1. Example: Retrieving a List of Privileges Supported on a Resource + + This example shows a client request for the DAV:supported-privilege- + set property on the resource http://www.example.com/papers/. The + value of the DAV:supported-privilege-set property is a tree of + supported privileges (using "[XML Namespace , localname]" to identify + each privilege): + + [DAV:, all] (aggregate, abstract) + | + +-- [DAV:, read] (aggregate) + | + +-- [DAV:, read-acl] (abstract) + +-- [DAV:, read-current-user-privilege-set] (abstract) + | + +-- [DAV:, write] (aggregate) + | + +-- [DAV:, write-acl] (abstract) + +-- [DAV:, write-properties] + +-- [DAV:, write-content] + | + +-- [DAV:, unlock] + + This privilege tree is not normative (except that it reflects the + normative aggregation rules given in Section 3.12), and many possible + privilege trees are possible. + + >> Request << + + PROPFIND /papers/ HTTP/1.1 + Host: www.example.com + Content-type: text/xml; charset="utf-8" + Content-Length: xxx + Depth: 0 + Authorization: Digest username="gclemm", + realm="users@example.com", nonce="...", + uri="/papers/", response="...", opaque="..." + + + + + + + + + + + + + + +Clemm, et al. Standards Track [Page 19] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + >> Response << + + HTTP/1.1 207 Multi-Status + + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + + + + + http://www.example.com/papers/ + + + + + + + + Any operation + + + + + Read any object + + + + + Read ACL + + + + + + + + Read current user privilege set property + + + + + + + Write any object + + + + + + + +Clemm, et al. Standards Track [Page 20] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + Write ACL + + + + + + + Write properties + + + + + + Write resource content + + + + + + + Unlock resource + + + + + + HTTP/1.1 200 OK + + + + +5.4. DAV:current-user-privilege-set + + DAV:current-user-privilege-set is a protected property containing the + exact set of privileges (as computed by the server) granted to the + currently authenticated HTTP user. Aggregate privileges and their + contained privileges are listed. A user-agent can use the value of + this property to adjust its user interface to make actions + inaccessible (e.g., by graying out a menu item or button) for which + the current principal does not have permission. This property is + also useful for determining what operations the current principal can + perform, without having to actually execute an operation. + + + + + + + + + +Clemm, et al. Standards Track [Page 21] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + If the current user is granted a specific privilege, that privilege + must belong to the set of privileges that may be set on this + resource. Therefore, each element in the DAV:current-user- + privilege-set property MUST identify a non-abstract privilege from + the DAV:supported-privilege-set property. + +5.4.1. Example: Retrieving the User's Current Set of Assigned + Privileges + + Continuing the example from Section 5.3.1, this example shows a + client requesting the DAV:current-user-privilege-set property from + the resource with URL http://www.example.com/papers/. The username + of the principal making the request is "khare", and Digest + authentication is used in the request. The principal with username + "khare" has been granted the DAV:read privilege. Since the DAV:read + privilege contains the DAV:read-acl and DAV:read-current-user- + privilege-set privileges (see Section 5.3.1), the principal with + username "khare" can read the ACL property, and the DAV:current- + user-privilege-set property. However, the DAV:all, DAV:read-acl, + DAV:write-acl and DAV:read-current-user-privilege-set privileges are + not listed in the value of DAV:current-user-privilege-set, since (for + this example) they are abstract privileges. DAV:write is not listed + since the principal with username "khare" is not listed in an ACE + granting that principal write permission. + + >> Request << + + PROPFIND /papers/ HTTP/1.1 + Host: www.example.com + Content-type: text/xml; charset="utf-8" + Content-Length: xxx + Depth: 0 + Authorization: Digest username="khare", + realm="users@example.com", nonce="...", + uri="/papers/", response="...", opaque="..." + + + + + + + + + + + + + + + + +Clemm, et al. Standards Track [Page 22] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + >> Response << + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + + + + + http://www.example.com/papers/ + + + + + + + HTTP/1.1 200 OK + + + + +5.5. DAV:acl + + This is a protected property that specifies the list of access + control entries (ACEs), which define what principals are to get what + privileges for this resource. + + + + Each DAV:ace element specifies the set of privileges to be either + granted or denied to a single principal. If the DAV:acl property is + empty, no principal is granted any privilege. + + + +5.5.1. ACE Principal + + The DAV:principal element identifies the principal to which this ACE + applies. + + + + The current user matches DAV:href only if that user is authenticated + as being (or being a member of) the principal identified by the URL + contained by that DAV:href. + + + + +Clemm, et al. Standards Track [Page 23] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + The current user always matches DAV:all. + + + + The current user matches DAV:authenticated only if authenticated. + + + + The current user matches DAV:unauthenticated only if not + authenticated. + + + + DAV:all is the union of DAV:authenticated, and DAV:unauthenticated. + For a given request, the user matches either DAV:authenticated, or + DAV:unauthenticated, but not both (that is, DAV:authenticated and + DAV:unauthenticated are disjoint sets). + + The current user matches a DAV:property principal in a DAV:acl + property of a resource only if the value of the identified property + of that resource contains at most one DAV:href XML element, the URI + value of DAV:href identifies a principal, and the current user is + authenticated as being (or being a member of) that principal. For + example, if the DAV:property element contained , the + current user would match the DAV:property principal only if the + current user is authenticated as matching the principal identified by + the DAV:owner property of the resource. + + + + The current user matches DAV:self in a DAV:acl property of the + resource only if that resource is a principal and that principal + matches the current user or, if the principal is a group, a member of + that group matches the current user. + + + + Some servers may support ACEs applying to those users NOT matching + the current principal, e.g., all users not in a particular group. + This can be done by wrapping the DAV:principal element with + DAV:invert. + + + + + + + + + + +Clemm, et al. Standards Track [Page 24] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +5.5.2. ACE Grant and Deny + + Each DAV:grant or DAV:deny element specifies the set of privileges to + be either granted or denied to the specified principal. A DAV:grant + or DAV:deny element of the DAV:acl of a resource MUST only contain + non-abstract elements specified in the DAV:supported-privilege-set of + that resource. + + + + + +5.5.3. ACE Protection + + A server indicates an ACE is protected by including the DAV:protected + element in the ACE. If the ACL of a resource contains an ACE with a + DAV:protected element, an attempt to remove that ACE from the ACL + MUST fail. + + + +5.5.4. ACE Inheritance + + The presence of a DAV:inherited element indicates that this ACE is + inherited from another resource that is identified by the URL + contained in a DAV:href element. An inherited ACE cannot be modified + directly, but instead the ACL on the resource from which it is + inherited must be modified. + + Note that ACE inheritance is not the same as ACL initialization. ACL + initialization defines the ACL that a newly created resource will use + (if not specified). ACE inheritance refers to an ACE that is + logically shared - where an update to the resource containing an ACE + will affect the ACE of each resource that inherits that ACE. The + method by which ACLs are initialized or by which ACEs are inherited + is not defined by this document. + + + +5.5.5. Example: Retrieving a Resource's Access Control List + + Continuing the example from Sections 5.3.1 and 5.4.1, this example + shows a client requesting the DAV:acl property from the resource with + URL http://www.example.com/papers/. There are two ACEs defined in + this ACL: + + + + + + +Clemm, et al. Standards Track [Page 25] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + ACE #1: The group identified by URL http://www.example.com/acl/ + groups/maintainers (the group of site maintainers) is granted + DAV:write privilege. Since (for this example) DAV:write contains the + DAV:write-acl privilege (see Section 5.3.1), this means the + "maintainers" group can also modify the access control list. + + ACE #2: All principals (DAV:all) are granted the DAV:read privilege. + Since (for this example) DAV:read contains DAV:read-acl and + DAV:read-current-user-privilege-set, this means all users (including + all members of the "maintainers" group) can read the DAV:acl property + and the DAV:current-user-privilege-set property. + + >> Request << + + PROPFIND /papers/ HTTP/1.1 + Host: www.example.com + Content-type: text/xml; charset="utf-8" + Content-Length: xxx + Depth: 0 + Authorization: Digest username="masinter", + realm="users@example.com", nonce="...", + uri="/papers/", response="...", opaque="..." + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + + + + http://www.example.com/papers/ + + + + + + http://www.example.com/acl/groups/maintainers + + + + + + +Clemm, et al. Standards Track [Page 26] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + + + + + + + + + + + + + HTTP/1.1 200 OK + + + + +5.6. DAV:acl-restrictions + + This protected property defines the types of ACLs supported by this + server, to avoid clients needlessly getting errors. When a client + tries to set an ACL via the ACL method, the server may reject the + attempt to set the ACL as specified. The following properties + indicate the restrictions the client must observe before setting an + ACL: + + Deny ACEs are not supported + + Inverted ACEs are not supported + + All deny ACEs must occur before any grant ACEs + + Indicates which principals are required to be + present + + + + +5.6.1. DAV:grant-only + + This element indicates that ACEs with deny clauses are not allowed. + + + + + + + + +Clemm, et al. Standards Track [Page 27] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +5.6.2. DAV:no-invert ACE Constraint + + This element indicates that ACEs with the element are not + allowed. + + + +5.6.3. DAV:deny-before-grant + + This element indicates that all deny ACEs must precede all grant + ACEs. + + + +5.6.4. Required Principals + + The required principal elements identify which principals must have + an ACE defined in the ACL. + + + + For example, the following element requires that the ACL contain a + + DAV:owner property ACE: + + + + + +5.6.5. Example: Retrieving DAV:acl-restrictions + + In this example, the client requests the value of the DAV:acl- + restrictions property. Digest authentication provides credentials + for the principal operating the client. + + >> Request << + + PROPFIND /papers/ HTTP/1.1 + Host: www.example.com + Content-type: text/xml; charset="utf-8" + Content-Length: xxx + Depth: 0 + Authorization: Digest username="srcarter", + realm="users@example.com", nonce="...", + uri="/papers/", response="...", opaque="..." + + + + +Clemm, et al. Standards Track [Page 28] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + + + + + http://www.example.com/papers/ + + + + + + + + + + HTTP/1.1 200 OK + + + + +5.7. DAV:inherited-acl-set + + This protected property contains a set of URLs that identify other + resources that also control the access to this resource. To have a + privilege on a resource, not only must the ACL on that resource + (specified in the DAV:acl property of that resource) grant the + privilege, but so must the ACL of each resource identified in the + DAV:inherited-acl-set property of that resource. Effectively, the + privileges granted by the current ACL are ANDed with the privileges + granted by each inherited ACL. + + + + + + + + + + +Clemm, et al. Standards Track [Page 29] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +5.8. DAV:principal-collection-set + + This protected property of a resource contains a set of URLs that + identify the root collections that contain the principals that are + available on the server that implements this resource. A WebDAV + Access Control Protocol user agent could use the contents of + DAV:principal-collection-set to retrieve the DAV:displayname property + (specified in Section 13.2 of [RFC2518]) of all principals on that + server, thereby yielding human-readable names for each principal that + could be displayed in a user interface. + + + + Since different servers can control different parts of the URL + namespace, different resources on the same host MAY have different + DAV:principal-collection-set values. The collections specified in + the DAV:principal-collection-set MAY be located on different hosts + from the resource. The URLs in DAV:principal-collection-set SHOULD be + http or https scheme URLs. For security and scalability reasons, a + server MAY report only a subset of the entire set of known principal + collections, and therefore clients should not assume they have + retrieved an exhaustive listing. Additionally, a server MAY elect to + report none of the principal collections it knows about, in which + case the property value would be empty. + + The value of DAV:principal-collection-set gives the scope of the + DAV:principal-property-search REPORT (defined in Section 9.4). + Clients use the DAV:principal-property-search REPORT to populate + their user interface with a list of principals. Therefore, servers + that limit a client's ability to obtain principal information will + interfere with the client's ability to manipulate access control + lists, due to the difficulty of getting the URL of a principal for + use in an ACE. + +5.8.1. Example: Retrieving DAV:principal-collection-set + + In this example, the client requests the value of the DAV:principal- + collection-set property on the collection resource identified by URL + http://www.example.com/papers/. The property contains the two URLs, + http://www.example.com/acl/users/ and http:// + www.example.com/acl/groups/, both wrapped in DAV:href XML elements. + Digest authentication provides credentials for the principal + operating the client. + + + + + + + + +Clemm, et al. Standards Track [Page 30] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + The client might reasonably follow this request with two separate + PROPFIND requests to retrieve the DAV:displayname property of the + members of the two collections (/acl/users and /acl/groups). This + information could be used when displaying a user interface for + creating access control entries. + + >> Request << + + PROPFIND /papers/ HTTP/1.1 + Host: www.example.com + Content-type: text/xml; charset="utf-8" + Content-Length: xxx + Depth: 0 + Authorization: Digest username="yarong", + realm="users@example.com", nonce="...", + uri="/papers/", response="...", opaque="..." + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + + + + + http://www.example.com/papers/ + + + + http://www.example.com/acl/users/ + http://www.example.com/acl/groups/ + + + HTTP/1.1 200 OK + + + + + + + + + +Clemm, et al. Standards Track [Page 31] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +5.9. Example: PROPFIND to retrieve access control properties + + The following example shows how access control information can be + retrieved by using the PROPFIND method to fetch the values of the + DAV:owner, DAV:supported-privilege-set, DAV:current-user-privilege- + set, and DAV:acl properties. + + >> Request << + + PROPFIND /top/container/ HTTP/1.1 + Host: www.example.com + Content-type: text/xml; charset="utf-8" + Content-Length: xxx + Depth: 0 + Authorization: Digest username="ejw", + realm="users@example.com", nonce="...", + uri="/top/container/", response="...", opaque="..." + + + + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + + + + + http://www.example.com/top/container/ + + + + http://www.example.com/users/gclemm + + + + + + + + +Clemm, et al. Standards Track [Page 32] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + + Any operation + + + + + Read any object + + + + + + + Write any object + + + + + Create an object + + + + + + Update an object + + + + + + + Delete an object + + + + + + Read the ACL + + + + + + Write the ACL + + + + + + + +Clemm, et al. Standards Track [Page 33] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + + + + + + + + http://www.example.com/users/esedlar + + + + + + + + + + http://www.example.com/groups/mrktng + + + + + + + + + + + + + + + + + + + + + http://www.example.com/top + + + + + HTTP/1.1 200 OK + + + + + + + +Clemm, et al. Standards Track [Page 34] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + The value of the DAV:owner property is a single DAV:href XML element + containing the URL of the principal that owns this resource. + + The value of the DAV:supported-privilege-set property is a tree of + supported privileges (using "[XML Namespace , localname]" to identify + each privilege): + + [DAV:, all] (aggregate, abstract) + | + +-- [DAV:, read] + +-- [DAV:, write] (aggregate, abstract) + | + +-- [http://www.example.com/acl, create] + +-- [http://www.example.com/acl, update] + +-- [http://www.example.com/acl, delete] + +-- [DAV:, read-acl] + +-- [DAV:, write-acl] + + The DAV:current-user-privilege-set property contains two privileges, + DAV:read, and DAV:read-acl. This indicates that the current + authenticated user only has the ability to read the resource, and + read the DAV:acl property on the resource. The DAV:acl property + contains a set of four ACEs: + + ACE #1: The principal identified by the URL http://www.example.com/ + users/esedlar is granted the DAV:read, DAV:write, and DAV:read-acl + privileges. + + ACE #2: The principals identified by the URL http://www.example.com/ + groups/mrktng are denied the DAV:read privilege. In this example, + the principal URL identifies a group. + + ACE #3: In this ACE, the principal is a property principal, + specifically the DAV:owner property. When evaluating this ACE, the + value of the DAV:owner property is retrieved, and is examined to see + if it contains a DAV:href XML element. If so, the URL within the + DAV:href element is read, and identifies a principal. In this ACE, + the owner is granted DAV:read-acl, and DAV:write-acl privileges. + + ACE #4: This ACE grants the DAV:all principal (all users) the + DAV:read privilege. This ACE is inherited from the resource http:// + www.example.com/top, the parent collection of this resource. + + + + + + + + + +Clemm, et al. Standards Track [Page 35] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +6. ACL Evaluation + + WebDAV ACLs are evaluated in similar manner as ACLs on Windows NT and + in NFSv4 [RFC3530]). An ACL is evaluated to determine whether or not + access will be granted for a WebDAV request. ACEs are maintained in + a particular order, and are evaluated until all of the permissions + required by the current request have been granted, at which point the + ACL evaluation is terminated and access is granted. If, during ACL + evaluation, a ACE (matching the current user) is encountered + for a privilege which has not yet been granted, the ACL evaluation is + terminated and access is denied. Failure to have all required + privileges granted results in access being denied. + + Note that the semantics of many other existing ACL systems may be + represented via this mechanism, by mixing deny and grant ACEs. For + example, consider the standard "rwx" privilege scheme used by UNIX. + In this scheme, if the current user is the owner of the file, access + is granted if the corresponding privilege bit is set and denied if + not set, regardless of the permissions set on the file's group and + for the world. An ACL for UNIX permissions of "r--rw-r--" might be + constructed like: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Clemm, et al. Standards Track [Page 36] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + + + + + + + + + + + + + + + + + and the would be defined as: + + + + + + + + + Note that the client can still get errors from a UNIX server in spite + of obeying the , including + (adding an ACE specifying a principal other than the ones in the ACL + above) or (by trying to reorder the ACEs in the + example above), as these particular implementation semantics are too + complex to be captured with the simple (but general) declarative + restrictions. + +7. Access Control and existing methods + + This section defines the impact of access control functionality on + existing methods. + +7.1. Any HTTP method + +7.1.1. Error Handling + + The WebDAV ACL mechanism requires the usage of HTTP method + "preconditions" as described in section 1.6 of RFC3253 for ALL HTTP + methods. All HTTP methods have an additional precondition called + DAV:need-privileges. If an HTTP method fails due to insufficient + privileges, the response body to the "403 Forbidden" error MUST + contain the element, which in turn contains the + + + +Clemm, et al. Standards Track [Page 37] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + element, which contains one or more + elements indicating which resource had insufficient + privileges, and what the lacking privileges were: + + + + + Since some methods require multiple permissions on multiple + resources, this information is needed to resolve any ambiguity. + There is no requirement that all privilege violations be reported - + for implementation reasons, some servers may only report the first + privilege violation. For example: + + >> Request << + + MOVE /a/b/ HTTP/1.1 + Host: www.example.com + Destination: http://www.example.com/c/d + + >> Response << + + HTTP/1.1 403 Forbidden + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + + + + + /a + + + + /c + + + + + +7.2. OPTIONS + + If the server supports access control, it MUST return "access- + control" as a field in the DAV response header from an OPTIONS + request on any resource implemented by that server. A value of + "access-control" in the DAV header MUST indicate that the server + supports all MUST level requirements and REQUIRED features specified + in this document. + + + + + +Clemm, et al. Standards Track [Page 38] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +7.2.1. Example - OPTIONS + + >> Request << + + OPTIONS /foo.html HTTP/1.1 + Host: www.example.com + Content-Length: 0 + + >> Response << + + HTTP/1.1 200 OK + DAV: 1, 2, access-control + Allow: OPTIONS, GET, PUT, PROPFIND, PROPPATCH, ACL + + In this example, the OPTIONS response indicates that the server + supports access control and that /foo.html can have its access + control list modified by the ACL method. + +7.3. MOVE + + When a resource is moved from one location to another due to a MOVE + request, the non-inherited and non-protected ACEs in the DAV:acl + property of the resource MUST NOT be modified, or the MOVE request + fails. Handling of inherited and protected ACEs is intentionally + undefined to give server implementations flexibility in how they + implement ACE inheritance and protection. + +7.4. COPY + + The DAV:acl property on the resource at the destination of a COPY + MUST be the same as if the resource was created by an individual + resource creation request (e.g., MKCOL, PUT). Clients wishing to + preserve the DAV:acl property across a copy need to read the DAV:acl + property prior to the COPY, then perform an ACL operation on the new + resource at the destination to restore, insofar as this is possible, + the original access control list. + +7.5. LOCK + + A lock on a resource ensures that only the lock owner can modify ACEs + that are not inherited and not protected (these are the only ACEs + that a client can modify with an ACL request). A lock does not + protect inherited or protected ACEs, since a client cannot modify + them with an ACL request on that resource. + + + + + + + +Clemm, et al. Standards Track [Page 39] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +8. Access Control Methods + +8.1. ACL + + The ACL method modifies the access control list (which can be read + via the DAV:acl property) of a resource. Specifically, the ACL + method only permits modification to ACEs that are not inherited, and + are not protected. An ACL method invocation modifies all non- + inherited and non-protected ACEs in a resource's access control list + to exactly match the ACEs contained within in the DAV:acl XML element + (specified in Section 5.5) of the request body. An ACL request body + MUST contain only one DAV:acl XML element. Unless the non-inherited + and non-protected ACEs of the DAV:acl property of the resource can be + updated to be exactly the value specified in the ACL request, the ACL + request MUST fail. + + It is possible that the ACEs visible to the current user in the + DAV:acl property may only be a portion of the complete set of ACEs on + that resource. If this is the case, an ACL request only modifies the + set of ACEs visible to the current user, and does not affect any + non-visible ACE. + + In order to avoid overwriting DAV:acl changes by another client, a + client SHOULD acquire a WebDAV lock on the resource before retrieving + the DAV:acl property of a resource that it intends on updating. + + Implementation Note: Two common operations are to add or remove an + ACE from an existing access control list. To accomplish this, a + client uses the PROPFIND method to retrieve the value of the + DAV:acl property, then parses the returned access control list to + remove all inherited and protected ACEs (these ACEs are tagged + with the DAV:inherited and DAV:protected XML elements). In the + remaining set of non-inherited, non-protected ACEs, the client can + add or remove one or more ACEs before submitting the final ACE set + in the request body of the ACL method. + +8.1.1. ACL Preconditions + + An implementation MUST enforce the following constraints on an ACL + request. If the constraint is violated, a 403 (Forbidden) or 409 + (Conflict) response MUST be returned and the indicated XML element + MUST be returned as a child of a top level DAV:error element in an + XML response body. + + Though these status elements are generally expressed as empty XML + elements (and are defined as EMPTY in the DTD), implementations MAY + return additional descriptive XML elements as children of the status + + + + +Clemm, et al. Standards Track [Page 40] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + element. Clients MUST be able to accept children of these status + elements. Clients that do not understand the additional XML elements + should ignore them. + + (DAV:no-ace-conflict): The ACEs submitted in the ACL request MUST NOT + conflict with each other. This is a catchall error code indicating + that an implementation-specific ACL restriction has been violated. + + (DAV:no-protected-ace-conflict): The ACEs submitted in the ACL + request MUST NOT conflict with the protected ACEs on the resource. + For example, if the resource has a protected ACE granting DAV:write + to a given principal, then it would not be consistent if the ACL + request submitted an ACE denying DAV:write to the same principal. + + (DAV:no-inherited-ace-conflict): The ACEs submitted in the ACL + request MUST NOT conflict with the inherited ACEs on the resource. + For example, if the resource inherits an ACE from its parent + collection granting DAV:write to a given principal, then it would not + be consistent if the ACL request submitted an ACE denying DAV:write + to the same principal. Note that reporting of this error will be + implementation-dependent. Implementations MUST either report this + error or allow the ACE to be set, and then let normal ACE evaluation + rules determine whether the new ACE has any impact on the privileges + available to a specific principal. + + (DAV:limited-number-of-aces): The number of ACEs submitted in the ACL + request MUST NOT exceed the number of ACEs allowed on that resource. + However, ACL-compliant servers MUST support at least one ACE granting + privileges to a single principal, and one ACE granting privileges to + a group. + + (DAV:deny-before-grant): All non-inherited deny ACEs MUST precede all + non-inherited grant ACEs. + + (DAV:grant-only): The ACEs submitted in the ACL request MUST NOT + include a deny ACE. This precondition applies only when the ACL + restrictions of the resource include the DAV:grant-only constraint + (defined in Section 5.6.1). + + (DAV:no-invert): The ACL request MUST NOT include a DAV:invert + element. This precondition applies only when the ACL semantics of + the resource includes the DAV:no-invert constraint (defined in + Section 5.6.2). + + (DAV:no-abstract): The ACL request MUST NOT attempt to grant or deny + an abstract privilege (see Section 5.3). + + + + + +Clemm, et al. Standards Track [Page 41] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + (DAV:not-supported-privilege): The ACEs submitted in the ACL request + MUST be supported by the resource. + + (DAV:missing-required-principal): The result of the ACL request MUST + have at least one ACE for each principal identified in a + DAV:required-principal XML element in the ACL semantics of that + resource (see Section 5.5). + + (DAV:recognized-principal): Every principal URL in the ACL request + MUST identify a principal resource. + + (DAV:allowed-principal): The principals specified in the ACEs + submitted in the ACL request MUST be allowed as principals for the + resource. For example, a server where only authenticated principals + can access resources would not allow the DAV:all or + DAV:unauthenticated principals to be used in an ACE, since these + would allow unauthenticated access to resources. + +8.1.2. Example: the ACL method + + In the following example, user "fielding", authenticated by + information in the Authorization header, grants the principal + identified by the URL http://www.example.com/users/esedlar (i.e., the + user "esedlar") read and write privileges, grants the owner of the + resource read-acl and write-acl privileges, and grants everyone read + privileges. + + >> Request << + + ACL /top/container/ HTTP/1.1 + Host: www.example.com + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + Authorization: Digest username="fielding", + realm="users@example.com", nonce="...", + uri="/top/container/", response="...", opaque="..." + + + + + + http://www.example.com/users/esedlar + + + + + + + + + +Clemm, et al. Standards Track [Page 42] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + + + + + + + + + + + + + + + + + + >> Response << + + HTTP/1.1 200 OK + +8.1.3. Example: ACL method failure due to protected ACE conflict + + In the following request, user "fielding", authenticated by + information in the Authorization header, attempts to deny the + principal identified by the URL http://www.example.com/users/esedlar + (i.e., the user "esedlar") write privileges. Prior to the request, + the DAV:acl property on the resource contained a protected ACE (see + Section 5.5.3) granting DAV:owner the DAV:read and DAV:write + privileges. The principal identified by URL http://www.example.com/ + users/esedlar is the owner of the resource. The ACL method + invocation fails because the submitted ACE conflicts with the + protected ACE, thus violating the semantics of ACE protection. + + >> Request << + + ACL /top/container/ HTTP/1.1 + Host: www.example.com + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + Authorization: Digest username="fielding", + realm="users@example.com", nonce="...", + uri="/top/container/", response="...", opaque="..." + + + + + + + + +Clemm, et al. Standards Track [Page 43] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + http://www.example.com/users/esedlar + + + + + + + + >> Response << + + HTTP/1.1 403 Forbidden + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + + + + + + +8.1.4. Example: ACL method failure due to an inherited ACE conflict + + In the following request, user "ejw", authenticated by information in + the Authorization header, tries to change the access control list on + the resource http://www.example.com/top/index.html. This resource + has two inherited ACEs. + + Inherited ACE #1 grants the principal identified by URL http:// + www.example.com/users/ejw (i.e., the user "ejw") http:// + www.example.com/privs/write-all and DAV:read-acl privileges. On this + server, http://www.example.com/privs/write-all is an aggregate + privilege containing DAV:write, and DAV:write-acl. + + Inherited ACE #2 grants principal DAV:all the DAV:read privilege. + + The request attempts to set a (non-inherited) ACE, denying the + principal identified by the URL http://www.example.com/users/ejw + (i.e., the user "ejw") DAV:write permission. This conflicts with + inherited ACE #1. Note that the decision to report an inherited ACE + conflict is specific to this server implementation. Another server + implementation could have allowed the new ACE to be set, and then + used normal ACE evaluation rules to determine whether the new ACE has + any impact on the privileges available to a principal. + + + + + + + + + +Clemm, et al. Standards Track [Page 44] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + >> Request << + + ACL /top/index.html HTTP/1.1 + Host: www.example.com + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + Authorization: Digest username="ejw", + realm="users@example.com", nonce="...", + uri="/top/index.html", response="...", opaque="..." + + + + + + http://www.example.com/users/ejw + + + + + + >> Response << + + HTTP/1.1 403 Forbidden + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + + + + + + +8.1.5. Example: ACL method failure due to an attempt to set grant and + deny in a single ACE + + In this example, user "ygoland", authenticated by information in the + Authorization header, tries to change the access control list on the + resource http://www.example.com/diamond/engagement-ring.gif. The ACL + request includes a single, syntactically and semantically incorrect + ACE, which attempts to grant the group identified by the URL http:// + www.example.com/users/friends DAV:read privilege and deny the + principal identified by URL http://www.example.com/users/ygoland-so + (i.e., the user "ygoland-so") DAV:read privilege. However, it is + illegal to have multiple principal elements, as well as both a grant + and deny element in the same ACE, so the request fails due to poor + syntax. + + + + + + +Clemm, et al. Standards Track [Page 45] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + >> Request << + + ACL /diamond/engagement-ring.gif HTTP/1.1 + Host: www.example.com + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + Authorization: Digest username="ygoland", + realm="users@example.com", nonce="...", + uri="/diamond/engagement-ring.gif", response="...", + opaque="..." + + + + + + http://www.example.com/users/friends + + + + http://www.example.com/users/ygoland-so + + + + + + >> Response << + + HTTP/1.1 400 Bad Request + Content-Length: 0 + + Note that if the request had been divided into two ACEs, one to + grant, and one to deny, the request would have been syntactically + well formed. + +9. Access Control Reports + +9.1. REPORT Method + + The REPORT method (defined in Section 3.6 of [RFC3253]) provides an + extensible mechanism for obtaining information about a resource. + Unlike the PROPFIND method, which returns the value of one or more + named properties, the REPORT method can involve more complex + processing. REPORT is valuable in cases where the server has access + to all of the information needed to perform the complex request (such + as a query), and where it would require multiple requests for the + client to retrieve the information needed to perform the same + request. + + + + +Clemm, et al. Standards Track [Page 46] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + A server that supports the WebDAV Access Control Protocol MUST + support the DAV:expand-property report (defined in Section 3.8 of + [RFC3253]). + +9.2. DAV:acl-principal-prop-set Report + + The DAV:acl-principal-prop-set report returns, for all principals in + the DAV:acl property (of the Request-URI) that are identified by + http(s) URLs or by a DAV:property principal, the value of the + properties specified in the REPORT request body. In the case where a + principal URL appears multiple times, the DAV:acl-principal-prop-set + report MUST return the properties for that principal only once. + Support for this report is REQUIRED. + + One expected use of this report is to retrieve the human readable + name (found in the DAV:displayname property) of each principal found + in an ACL. This is useful for constructing user interfaces that show + each ACE in a human readable form. + + Marshalling + + The request body MUST be a DAV:acl-principal-prop-set XML element. + + + ANY value: a sequence of one or more elements, with at most one + DAV:prop element. + prop: see RFC 2518, Section 12.11 + + This report is only defined when the Depth header has value "0"; + other values result in a 400 (Bad Request) error response. Note + that [RFC3253], Section 3.6, states that if the Depth header is + not present, it defaults to a value of "0". + + The response body for a successful request MUST be a + DAV:multistatus XML element (i.e., the response uses the same + format as the response for PROPFIND). In the case where there are + no response elements, the returned multistatus XML element is + empty. + + multistatus: see RFC 2518, Section 12.9 + + The response body for a successful DAV:acl-principal-prop-set + REPORT request MUST contain a DAV:response element for each + principal identified by an http(s) URL listed in a DAV:principal + XML element of an ACE within the DAV:acl property of the resource + identified by the Request-URI. + + + + + +Clemm, et al. Standards Track [Page 47] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + Postconditions: + + (DAV:number-of-matches-within-limits): The number of matching + principals must fall within server-specific, predefined limits. + For example, this condition might be triggered if a search + specification would cause the return of an extremely large number + of responses. + +9.2.1. Example: DAV:acl-principal-prop-set Report + + Resource http://www.example.com/index.html has an ACL with three + ACEs: + + ACE #1: All principals (DAV:all) have DAV:read and DAV:read-current- + user-privilege-set access. + + ACE #2: The principal identified by http://www.example.com/people/ + gstein (the user "gstein") is granted DAV:write, DAV:write-acl, + DAV:read-acl privileges. + + ACE #3: The group identified by http://www.example.com/groups/authors + (the "authors" group) is granted DAV:write and DAV:read-acl + privileges. + + The following example shows a DAV:acl-principal-prop-set report + requesting the DAV:displayname property. It returns the value of + DAV:displayname for resources http://www.example.com/people/gstein + and http://www.example.com/groups/authors , but not for DAV:all, + since this is not an http(s) URL. + + >> Request << + + REPORT /index.html HTTP/1.1 + Host: www.example.com + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + Depth: 0 + + + + + + + + + + + + + + +Clemm, et al. Standards Track [Page 48] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + >> Response << + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.example.com/people/gstein + + + Greg Stein + + HTTP/1.1 200 OK + + + + http://www.example.com/groups/authors + + + Site authors + + HTTP/1.1 200 OK + + + + +9.3. DAV:principal-match REPORT + + The DAV:principal-match REPORT is used to identify all members (at + any depth) of the collection identified by the Request-URI that are + principals and that match the current user. In particular, if the + collection contains principals, the report can be used to identify + all members of the collection that match the current user. + Alternatively, if the collection contains resources that have a + property that identifies a principal (e.g., DAV:owner), the report + can be used to identify all members of the collection whose property + identifies a principal that matches the current user. For example, + this report can return all of the resources in a collection hierarchy + that are owned by the current user. Support for this report is + REQUIRED. + + Marshalling: + + The request body MUST be a DAV:principal-match XML element. + + + + + +Clemm, et al. Standards Track [Page 49] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + ANY value: an element whose value identifies a property. The + expectation is the value of the named property typically contains + an href element that contains the URI of a principal + + prop: see RFC 2518, Section 12.11 + + This report is only defined when the Depth header has value "0"; + other values result in a 400 (Bad Request) error response. Note + that [RFC3253], Section 3.6, states that if the Depth header is + not present, it defaults to a value of "0". The response body for + a successful request MUST be a DAV:multistatus XML element. In + the case where there are no response elements, the returned + multistatus XML element is empty. + + multistatus: see RFC 2518, Section 12.9 + + The response body for a successful DAV:principal-match REPORT + request MUST contain a DAV:response element for each member of the + collection that matches the current user. When the + DAV:principal-property element is used, a match occurs if the + current user is matched by the principal identified by the URI + found in the DAV:href element of the property identified by the + DAV:principal-property element. When the DAV:self element is used + in a DAV:principal-match report issued against a group, it matches + the group if a member identifies the same principal as the current + user. + + If DAV:prop is specified in the request body, the properties + specified in the DAV:prop element MUST be reported in the + DAV:response elements. + +9.3.1. Example: DAV:principal-match REPORT + + The following example identifies the members of the collection + identified by the URL http://www.example.com/doc that are owned by + the current user. The current user ("gclemm") is authenticated using + Digest authentication. + + >> Request << + + REPORT /doc/ HTTP/1.1 + Host: www.example.com + Authorization: Digest username="gclemm", + realm="users@example.com", nonce="...", + uri="/papers/", response="...", opaque="..." + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + Depth: 0 + + + +Clemm, et al. Standards Track [Page 50] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.example.com/doc/foo.html + HTTP/1.1 200 OK + + + http://www.example.com/doc/img/bar.gif + HTTP/1.1 200 OK + + + +9.4. DAV:principal-property-search REPORT + + The DAV:principal-property-search REPORT performs a search for all + principals whose properties contain character data that matches the + search criteria specified in the request. One expected use of this + report is to discover the URL of a principal associated with a given + person or group by searching for them by name. This is done by + searching over DAV:displayname, which is defined on all principals. + + The actual search method (exact matching vs. substring matching vs, + prefix-matching, case-sensitivity) deliberately is left to the server + implementation to allow implementation on a wide set of possible user + management systems. In cases where the implementation of + DAV:principal-property-search is not constrained by the semantics of + an underlying user management repository, preferred default semantics + are caseless substring matches. + + For implementation efficiency, servers do not typically support + searching on all properties. A search requesting properties that are + not searchable for a particular principal will not match that + principal. + + Support for the DAV:principal-property-search report is REQUIRED. + + + +Clemm, et al. Standards Track [Page 51] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + Implementation Note: The value of a WebDAV property is a sequence + of well-formed XML, and hence can include any character in the + Unicode/ISO-10646 standard, that is, most known characters in + human languages. Due to the idiosyncrasies of case mapping across + human languages, implementation of case-insensitive matching is + non-trivial. Implementors of servers that do perform substring + matching are strongly encouraged to consult "The Unicode Standard" + [UNICODE4], especially Section 5.18, Subsection "Caseless + Matching", for guidance when implementing their case-insensitive + matching algorithms. + + Implementation Note: Some implementations of this protocol will + use an LDAP repository for storage of principal metadata. The + schema describing each attribute (akin to a WebDAV property) in an + LDAP repository specifies whether it supports case-sensitive or + caseless searching. One of the benefits of leaving the search + method to the discretion of the server implementation is the + default LDAP attribute search behavior can be used when + implementing the DAV:principal-property-search report. + + Marshalling: + + The request body MUST be a DAV:principal-property-search XML + element containing a search specification and an optional list of + properties. For every principal that matches the search + specification, the response will contain the value of the + requested properties on that principal. + + + + By default, the report searches all members (at any depth) of the + collection identified by the Request-URI. If DAV:apply-to- + principal-collection-set is specified in the request body, the + request is applied instead to each collection identified by the + DAV:principal-collection-set property of the resource identified + by the Request-URI. + + The DAV:property-search element contains a prop element + enumerating the properties to be searched and a match element, + containing the search string. + + + prop: see RFC 2518, Section 12.11 + + + + + + + +Clemm, et al. Standards Track [Page 52] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + Multiple property-search elements or multiple elements within a + DAV:prop element will be interpreted with a logical AND. + + This report is only defined when the Depth header has value "0"; + other values result in a 400 (Bad Request) error response. Note + that [RFC3253], Section 3.6, states that if the Depth header is + not present, it defaults to a value of "0". + + The response body for a successful request MUST be a + DAV:multistatus XML element. In the case where there are no + response elements, the returned multistatus XML element is empty. + + multistatus: see RFC 2518, Section 12.9 + + The response body for a successful DAV:principal-property-search + REPORT request MUST contain a DAV:response element for each + principal whose property values satisfy the search specification + given in DAV:principal-property-search. + + If DAV:prop is specified in the request body, the properties + specified in the DAV:prop element MUST be reported in the + DAV:response elements. + + Preconditions: + + None + + Postconditions: + + (DAV:number-of-matches-within-limits): The number of matching + principals must fall within server-specific, predefined limits. + For example, this condition might be triggered if a search + specification would cause the return of an extremely large number + of responses. + +9.4.1. Matching + + There are several cases to consider when matching strings. The + easiest case is when a property value is "simple" and has only + character information item content (see [REC-XML-INFOSET]). For + example, the search string "julian" would match the DAV:displayname + property with value "Julian Reschke". Note that the on-the-wire + marshaling of DAV:displayname in this case is: + + Julian Reschke + + + + + + +Clemm, et al. Standards Track [Page 53] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + The name of the property is encoded into the XML element information + item, and the character information item content of the property is + "Julian Reschke". + + A more complicated case occurs when properties have mixed content + (that is, compound values consisting of multiple child element items, + other types of information items, and character information item + content). Consider the property "aprop" in the namespace "http:// + www.example.com/props/", marshaled as: + + + {cdata 0}{cdata 1} + {cdata 2}{cdata 3} + + + In this case, matching is performed on each individual contiguous + sequence of character information items. In the example above, a + search string would be compared to the four following strings: + + {cdata 0} + {cdata 1} + {cdata 2} + {cdata 3} + + That is, four individual matches would be performed, one each for + {cdata 0}, {cdata 1}, {cdata 2}, and {cdata 3}. + +9.4.2. Example: successful DAV:principal-property-search REPORT + + In this example, the client requests the principal URLs of all users + whose DAV:displayname property contains the substring "doE" and whose + "title" property in the namespace "http://BigCorp.com/ns/" (that is, + their professional title) contains "Sales". In addition, the client + requests five properties to be returned with the matching principals: + + In the DAV: namespace: displayname + + In the http://www.example.com/ns/ namespace: department, phone, + office, salary + + + + + + + + + + + + +Clemm, et al. Standards Track [Page 54] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + The response shows that two principal resources meet the search + specification, "John Doe" and "Zygdoebert Smith". The property + "salary" in namespace "http://www.example.com/ns/" is not returned, + since the principal making the request does not have sufficient + access permissions to read this property. + + >> Request << + + REPORT /users/ HTTP/1.1 + Host: www.example.com + Content-Type: text/xml; charset=utf-8 + Content-Length: xxxx + Depth: 0 + + + + + + + + doE + + + + + + Sales + + + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Content-Type: text/xml; charset=utf-8 + Content-Length: xxxx + + + + + http://www.example.com/users/jdoe + + + + +Clemm, et al. Standards Track [Page 55] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + + John Doe + Widget Sales + 234-4567 + 209 + + HTTP/1.1 200 OK + + + + + + HTTP/1.1 403 Forbidden + + + + http://www.example.com/users/zsmith + + + Zygdoebert Smith + Gadget Sales + 234-7654 + 114 + + HTTP/1.1 200 OK + + + + + + HTTP/1.1 403 Forbidden + + + + +9.5. DAV:principal-search-property-set REPORT + + The DAV:principal-search-property-set REPORT identifies those + properties that may be searched using the DAV:principal-property- + search REPORT (defined in Section 9.4). + + Servers MUST support the DAV:principal-search-property-set REPORT on + all collections identified in the value of a DAV:principal- + collection-set property. + + An access control protocol user agent could use the results of the + DAV:principal-search-property-set REPORT to present a query interface + to the user for retrieving principals. + + + +Clemm, et al. Standards Track [Page 56] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + Support for this report is REQUIRED. + + Implementation Note: Some clients will have only limited screen + real estate for the display of lists of searchable properties. In + this case, a user might appreciate having the most frequently + searched properties be displayed on-screen, rather than having to + scroll through a long list of searchable properties. One + mechanism for signaling the most frequently searched properties is + to return them towards the start of a list of properties. A + client can then preferentially display the list of properties in + order, increasing the likelihood that the most frequently searched + properties will appear on-screen, and will not require scrolling + for their selection. + + Marshalling: + + The request body MUST be an empty DAV:principal-search-property- + set XML element. + + This report is only defined when the Depth header has value "0"; + other values result in a 400 (Bad Request) error response. Note + that [RFC3253], Section 3.6, states that if the Depth header is + not present, it defaults to a value of "0". + + The response body MUST be a DAV:principal-search-property-set XML + element, containing a DAV:principal-search-property XML element + for each property that may be searched with the DAV:principal- + property-search REPORT. A server MAY limit its response to just a + subset of the searchable properties, such as those likely to be + useful to an interactive access control client. + + + + Each DAV:principal-search-property XML element contains exactly + one searchable property, and a description of the property. + + + + The DAV:prop element contains one principal property on which the + server is able to perform a DAV:principal-property-search REPORT. + + prop: see RFC 2518, Section 12.11 + + + + + + + + +Clemm, et al. Standards Track [Page 57] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + The description element is a human-readable description of what + information this property represents. Servers MUST indicate the + human language of the description using the xml:lang attribute and + SHOULD consider the HTTP Accept-Language request header when + selecting one of multiple available languages. + + + +9.5.1. Example: DAV:principal-search-property-set REPORT + + In this example, the client determines the set of searchable + principal properties by requesting the DAV:principal-search- + property-set REPORT on the root of the server's principal URL + collection set, identified by http://www.example.com/users/. + + >> Request << + + REPORT /users/ HTTP/1.1 + Host: www.example.com + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + Accept-Language: en, de + Authorization: BASIC d2FubmFtYWs6cGFzc3dvcmQ= + Depth: 0 + + + + + >> Response << + + HTTP/1.1 200 OK + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + + + + + + + + Full name + + + + + + + + + + +Clemm, et al. Standards Track [Page 58] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + + Job title + + + +10. XML Processing + + Implementations of this specification MUST support the XML element + ignore rule, as specified in Section 23.3.2 of [RFC2518], and the XML + Namespace recommendation [REC-XML-NAMES]. + + Note that use of the DAV namespace is reserved for XML elements and + property names defined in a standards-track or Experimental IETF RFC. + +11. Internationalization Considerations + + In this specification, the only human-readable content can be found + in the description XML element, found within the DAV:supported- + privilege-set property. This element contains a human-readable + description of the capabilities controlled by a privilege. As a + result, the description element must be capable of representing + descriptions in multiple character sets. Since the description + element is found within a WebDAV property, it is represented on the + wire as XML [REC-XML], and hence can leverage XML's language tagging + and character set encoding capabilities. Specifically, XML + processors at minimum must be able to read XML elements encoded using + the UTF-8 [RFC3629] encoding of the ISO 10646 multilingual plane. + XML examples in this specification demonstrate use of the charset + parameter of the Content-Type header, as defined in [RFC3023], as + well as the XML "encoding" attribute, which together provide charset + identification information for MIME and XML processors. Furthermore, + this specification requires server implementations to tag description + fields with the xml:lang attribute (see Section 2.12 of [REC-XML]), + which specifies the human language of the description. Additionally, + server implementations should take into account the value of the + Accept-Language HTTP header to determine which description string to + return. + + For XML elements other than the description element, it is expected + that implementations will treat the property names, privilege names, + and values as tokens, and convert these tokens into human-readable + text in the user's language and character set when displayed to a + person. Only a generic WebDAV property display utility would display + these values in their raw form to a human user. + + For error reporting, we follow the convention of HTTP/1.1 status + codes, including with each status code a short, English description + of the code (e.g., 200 (OK)). While the possibility exists that a + + + +Clemm, et al. Standards Track [Page 59] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + poorly crafted user agent would display this message to a user, + internationalized applications will ignore this message, and display + an appropriate message in the user's language and character set. + + Further internationalization considerations for this protocol are + described in the WebDAV Distributed Authoring protocol specification + [RFC2518]. + +12. Security Considerations + + Applications and users of this access control protocol should be + aware of several security considerations, detailed below. In + addition to the discussion in this document, the security + considerations detailed in the HTTP/1.1 specification [RFC2616], the + WebDAV Distributed Authoring Protocol specification [RFC2518], and + the XML Media Types specification [RFC3023] should be considered in a + security analysis of this protocol. + +12.1. Increased Risk of Compromised Users + + In the absence of a mechanism for remotely manipulating access + control lists, if a single user's authentication credentials are + compromised, only those resources for which the user has access + permission can be read, modified, moved, or deleted. With the + introduction of this access control protocol, if a single compromised + user has the ability to change ACLs for a broad range of other users + (e.g., a super-user), the number of resources that could be altered + by a single compromised user increases. This risk can be mitigated + by limiting the number of people who have write-acl privileges across + a broad range of resources. + +12.2. Risks of the DAV:read-acl and DAV:current-user-privilege-set + Privileges + + The ability to read the access privileges (stored in the DAV:acl + property), or the privileges permitted the currently authenticated + user (stored in the DAV:current-user-privilege-set property) on a + resource may seem innocuous, since reading an ACL cannot possibly + affect the resource's state. However, if all resources have world- + readable ACLs, it is possible to perform an exhaustive search for + those resources that have inadvertently left themselves in a + vulnerable state, such as being world-writable. In particular, the + property retrieval method PROPFIND, executed with Depth infinity on + an entire hierarchy, is a very efficient way to retrieve the DAV:acl + or DAV:current-user-privilege-set properties. Once found, this + vulnerability can be exploited by a denial of service attack in which + the open resource is repeatedly overwritten. Alternately, writable + resources can be modified in undesirable ways. + + + +Clemm, et al. Standards Track [Page 60] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + To reduce this risk, read-acl privileges should not be granted to + unauthenticated principals, and restrictions on read-acl and read- + current-user-privilege-set privileges for authenticated principals + should be carefully analyzed when deploying this protocol. Access to + the current-user-privilege-set property will involve a tradeoff of + usability versus security. When the current-user-privilege-set is + visible, user interfaces are expected to provide enhanced information + concerning permitted and restricted operations, yet this information + may also indicate a vulnerability that could be exploited. + Deployment of this protocol will need to evaluate this tradeoff in + light of the requirements of the deployment environment. + +12.3. No Foreknowledge of Initial ACL + + In an effort to reduce protocol complexity, this protocol + specification intentionally does not address the issue of how to + manage or discover the initial ACL that is placed upon a resource + when it is created. The only way to discover the initial ACL is to + create a new resource, then retrieve the value of the DAV:acl + property. This assumes the principal creating the resource also has + been granted the DAV:read-acl privilege. + + As a result, it is possible that a principal could create a resource, + and then discover that its ACL grants privileges that are + undesirable. Furthermore, this protocol makes it possible (though + unlikely) that the creating principal could be unable to modify the + ACL, or even delete the resource. Even when the ACL can be modified, + there will be a short period of time when the resource exists with + the initial ACL before its new ACL can be set. + + Several factors mitigate this risk. Human principals are often aware + of the default access permissions in their editing environments and + take this into account when writing information. Furthermore, + default privilege policies are usually very conservative, limiting + the privileges granted by the initial ACL. + +13. Authentication + + Authentication mechanisms defined for use with HTTP and WebDAV also + apply to this WebDAV Access Control Protocol, in particular the Basic + and Digest authentication mechanisms defined in [RFC2617]. + Implementation of the ACL spec requires that Basic authentication, if + used, MUST only be supported over secure transport such as TLS. + + + + + + + + +Clemm, et al. Standards Track [Page 61] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +14. IANA Considerations + + This document uses the namespace defined by [RFC2518] for XML + elements. That is, this specification uses the "DAV:" URI namespace, + previously registered in the URI schemes registry. All other IANA + considerations mentioned in [RFC2518] are also applicable to this + specification. + +15. Acknowledgements + + This protocol is the collaborative product of the WebDAV ACL design + team: Bernard Chester, Geoff Clemm, Anne Hopkins, Barry Lind, Sean + Lyndersay, Eric Sedlar, Greg Stein, and Jim Whitehead. The authors + are grateful for the detailed review and comments provided by Jim + Amsden, Dylan Barrell, Gino Basso, Murthy Chintalapati, Lisa + Dusseault, Stefan Eissing, Tim Ellison, Yaron Goland, Dennis + Hamilton, Laurie Harper, Eckehard Hermann, Ron Jacobs, Chris Knight, + Remy Maucherat, Larry Masinter, Joe Orton, Peter Raymond, and Keith + Wannamaker. We thank Keith Wannamaker for the initial text of the + principal property search sections. Prior work on WebDAV access + control protocols has been performed by Yaron Goland, Paul Leach, + Lisa Dusseault, Howard Palmer, and Jon Radoff. We would like to + acknowledge the foundation laid for us by the authors of the DeltaV, + WebDAV and HTTP protocols upon which this protocol is layered, and + the invaluable feedback from the WebDAV working group. + +16. References + +16.1. Normative References + + [REC-XML] Bray, T., Paoli, J., Sperberg-McQueen, C. and E. + Maler, "Extensible Markup Language (XML) 1.0 + ((Third ed)", W3C REC REC-xml-20040204, February + 2004, . + + [REC-XML-INFOSET] Cowan, J. and R. Tobin, "XML Information Set + (Second Edition)", W3C REC REC-xml-infoset- + 20040204, February 2004, + . + + [REC-XML-NAMES] Bray, T., Hollander, D. and A. Layman, "Namespaces + in XML", W3C REC REC-xml-names-19990114, January + 1999, . + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + + +Clemm, et al. Standards Track [Page 62] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + [RFC2518] Goland, Y., Whitehead, E., Faizi, A., Carter, S. + and D. Jensen, "HTTP Extensions for Distributed + Authoring -- WEBDAV", RFC 2518, February 1999. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P. and T. Berners-Lee, + "Hypertext Transfer Protocol -- HTTP/1.1", RFC + 2616, June 1999. + + [RFC2617] Franks, J., Hallam-Baker, P., Hostetler, J., + Lawrence, S., Leach, P., Luotonen, A. and L. + Stewart, "HTTP Authentication: Basic and Digest + Access Authentication", RFC 2617, June 1999. + + [RFC3023] Murata, M., St.Laurent, S. and D. Kohn, "XML Media + Types", RFC 3023, January 2001. + + [RFC3253] Clemm, G., Amsden, J., Ellison, T., Kaler, C. and + J. Whitehead, "Versioning Extensions to WebDAV", + RFC 3253, March 2002. + + [RFC3530] Shepler, S., Ed., Callaghan, B., Robinson, D., + Thurlow, R., Beame, C., Eisler, M. and D. Noveck, + "Network File System (NFS) version 4 Protocol", RFC + 3530, April 2003. + + [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO + 10646", STD 63, RFC 3629 November 2003. + +16.2. Informative References + + [RFC2251] Wahl, M., Howes, T. and S. Kille, "Lightweight + Directory Access Protocol (v3)", RFC 2251, December + 1997. + + [RFC2255] Howes, T. and M. Smith, "The LDAP URL Format", RFC + 2255, December 1997. + + [UNICODE4] The Unicode Consortium, "The Unicode Standard - + Version 4.0", Addison-Wesley , August 2003, + . + ISBN 0321185781. + + + + + + + + + +Clemm, et al. Standards Track [Page 63] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +Appendix A. WebDAV XML Document Type Definition Addendum + + All XML elements defined in this Document Type Definition (DTD) + belong to the DAV namespace. This DTD should be viewed as an addendum + to the DTD provided in [RFC2518], section 23.1. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Clemm, et al. Standards Track [Page 64] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Clemm, et al. Standards Track [Page 65] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + + + + + + + + + + + + + + + + + + + + + + + + ANY value: a sequence of one or more elements, with at most one + DAV:prop element. + + + + ANY value: an element whose value identifies a property. The + expectation is the value of the named property typically contains + an href element that contains the URI of a principal + + + + + + + + + + + + + + + + + +Clemm, et al. Standards Track [Page 66] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +Appendix B. WebDAV Method Privilege Table (Normative) + + The following table of WebDAV methods (as defined in RFC 2518, 2616, + and 3253) clarifies which privileges are required for access for each + method. Note that the privileges listed, if denied, MUST cause + access to be denied. However, given that a specific implementation + MAY define an additional custom privilege to control access to + existing methods, having all of the indicated privileges does not + mean that access will be granted. Note that lack of the indicated + privileges does not imply that access will be denied, since a + particular implementation may use a sub-privilege aggregated under + the indicated privilege to control access. Privileges required refer + to the current resource being processed unless otherwise specified. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Clemm, et al. Standards Track [Page 67] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + +---------------------------------+---------------------------------+ + | METHOD | PRIVILEGES | + +---------------------------------+---------------------------------+ + | GET | | + | HEAD | | + | OPTIONS | | + | PUT (target exists) | on target | + | | resource | + | PUT (no target exists) | on parent collection | + | | of target | + | PROPPATCH | | + | ACL | | + | PROPFIND | (plus and | + | | as needed) | + | COPY (target exists) | , and | + | | on target | + | | resource | + | COPY (no target exists) | , on target | + | | collection | + | MOVE (no target exists) | on source collection | + | | and on target | + | | collection | + | MOVE (target exists) | As above, plus on | + | | the target collection | + | DELETE | on parent collection | + | LOCK (target exists) | | + | LOCK (no target exists) | on parent collection | + | MKCOL | on parent collection | + | UNLOCK | | + | CHECKOUT | | + | CHECKIN | | + | REPORT | (on all referenced | + | | resources) | + | VERSION-CONTROL | | + | MERGE | | + | MKWORKSPACE | on parent | + | | collection | + | BASELINE-CONTROL | and | + | | | + | MKACTIVITY | on parent | + | | collection | + +---------------------------------+---------------------------------+ + + + + + + + + +Clemm, et al. Standards Track [Page 68] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +Index + + A + ACL method 40 + + C + Condition Names + DAV:allowed-principal (pre) 42 + DAV:deny-before-grant (pre) 41 + DAV:grant-only (pre) 41 + DAV:limited-number-of-aces (pre) 41 + DAV:missing-required-principal (pre) 42 + DAV:no-abstract (pre) 41 + DAV:no-ace-conflict (pre) 41 + DAV:no-inherited-ace-conflict (pre) 41 + DAV:no-invert (pre) 41 + DAV:no-protected-ace-conflict (pre) 41 + DAV:not-supported-privilege (pre) 42 + DAV:number-of-matches-within-limits (post) 48, 53 + DAV:recognized-principal (pre) 42 + + D + DAV header + compliance class 'access-control' 38 + DAV:acl property 23 + DAV:acl-principal-prop-set report 48 + DAV:acl-restrictions property 27 + DAV:all privilege 13 + DAV:allowed-principal precondition 42 + DAV:alternate-URI-set property 14 + DAV:bind privilege 12 + DAV:current-user-privilege-set property 21 + DAV:deny-before-grant precondition 41 + DAV:grant-only precondition 41 + DAV:group property 18 + DAV:group-member-set property 14 + DAV:group-membership property 14 + DAV:inherited-acl-set property 29 + DAV:limited-number-of-aces precondition 41 + DAV:missing-required-principal precondition 42 + DAV:no-abstract precondition 41 + DAV:no-ace-conflict precondition 41 + DAV:no-inherited-ace-conflict precondition 41 + DAV:no-invert precondition 41 + DAV:no-protected-ace-conflict precondition 41 + DAV:not-supported-privilege precondition 42 + DAV:number-of-matches-within-limits postcondition 48, 53 + DAV:owner property 15 + + + +Clemm, et al. Standards Track [Page 69] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + DAV:principal resource type 13 + DAV:principal-collection-set property 30 + DAV:principal-match report 50 + DAV:principal-property-search 51 + DAV:principal-search-property-set 56 + DAV:principal-URL property 14 + DAV:read privilege 10 + DAV:read-acl privilege 11 + DAV:read-current-user-privilege-set privilege 12 + DAV:recognized-principal precondition 42 + DAV:supported-privilege-set property 18 + DAV:unbind privilege 12 + DAV:unlock privilege 11 + DAV:write privilege 10 + DAV:write-acl privilege 12 + DAV:write-content privilege 10 + DAV:write-properties privilege 10 + + M + Methods + ACL 40 + + P + Privileges + DAV:all 13 + DAV:bind 12 + DAV:read 10 + DAV:read-acl 11 + DAV:read-current-user-privilege-set 12 + DAV:unbind 12 + DAV:unlock 11 + DAV:write 10 + DAV:write-acl 12 + DAV:write-content 11 + DAV:write-properties 10 + Properties + DAV:acl 23 + DAV:acl-restrictions 27 + DAV:alternate-URI-set 14 + DAV:current-user-privilege-set 21 + DAV:group 18 + DAV:group-member-set 14 + DAV:group-membership 14 + DAV:inherited-acl-set 29 + DAV:owner 15 + DAV:principal-collection-set 30 + DAV:principal-URL 14 + DAV:supported-privilege-set 18 + + + +Clemm, et al. Standards Track [Page 70] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + + R + Reports + DAV:acl-principal-prop-set 47 + DAV:principal-match 49 + DAV:principal-property-search 51 + DAV:principal-search-property-set 56 + Resource Types + DAV:principal 13 + +Authors' Addresses + + Geoffrey Clemm + IBM + 20 Maguire Road + Lexington, MA 02421 + + EMail: geoffrey.clemm@us.ibm.com + + + Julian F. Reschke + greenbytes GmbH + Salzmannstrasse 152 + Muenster, NW 48159 + Germany + + EMail: julian.reschke@greenbytes.de + + + Eric Sedlar + Oracle Corporation + 500 Oracle Parkway + Redwood Shores, CA 94065 + + EMail: eric.sedlar@oracle.com + + + Jim Whitehead + U.C. Santa Cruz, Dept. of Computer Science + 1156 High Street + Santa Cruz, CA 95064 + + EMail: ejw@cse.ucsc.edu + + + + + + + + + +Clemm, et al. Standards Track [Page 71] + +RFC 3744 WebDAV Access Control Protocol May 2004 + + +Full Copyright Statement + + Copyright (C) The Internet Society (2004). This document is subject + to the rights, licenses and restrictions contained in BCP 78, and + except as set forth therein, the authors retain all their rights. + + This document and the information contained herein are provided on an + "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE + REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE + INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF + THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Intellectual Property + + The IETF takes no position regarding the validity or scope of any + Intellectual Property Rights or other rights that might be claimed + to pertain to the implementation or use of the technology + described in this document or the extent to which any license + under such rights might or might not be available; nor does it + represent that it has made any independent effort to identify any + such rights. Information on the procedures with respect to + rights in RFC documents can be found in BCP 78 and BCP 79. + + Copies of IPR disclosures made to the IETF Secretariat and any + assurances of licenses to be made available, or the result of an + attempt made to obtain a general license or permission for the use + of such proprietary rights by implementers or users of this + specification can be obtained from the IETF on-line IPR repository + at http://www.ietf.org/ipr. + + The IETF invites any interested party to bring to its attention + any copyrights, patents or patent applications, or other + proprietary rights that may cover technology that may be required + to implement this standard. Please address the information to the + IETF at ietf-ipr@ietf.org. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + +Clemm, et al. Standards Track [Page 72] + diff --git a/doc/rfc4791-caldav.txt b/doc/rfc4791-caldav.txt new file mode 100644 index 00000000..7a30bb21 --- /dev/null +++ b/doc/rfc4791-caldav.txt @@ -0,0 +1,5995 @@ + + + + + + +Network Working Group C. Daboo +Request for Comments: 4791 Apple +Category: Standards Track B. Desruisseaux + Oracle + L. Dusseault + CommerceNet + March 2007 + + + Calendaring Extensions to WebDAV (CalDAV) + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The IETF Trust (2007). + +Abstract + + This document defines extensions to the Web Distributed Authoring and + Versioning (WebDAV) protocol to specify a standard way of accessing, + managing, and sharing calendaring and scheduling information based on + the iCalendar format. This document defines the "calendar-access" + feature of CalDAV. + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 1] + +RFC 4791 CalDAV March 2007 + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 5 + 1.1. Notational Conventions . . . . . . . . . . . . . . . . . . 5 + 1.2. XML Namespaces and Processing . . . . . . . . . . . . . . 5 + 1.3. Method Preconditions and Postconditions . . . . . . . . . 6 + 2. Requirements Overview . . . . . . . . . . . . . . . . . . . . 6 + 3. Calendaring Data Model . . . . . . . . . . . . . . . . . . . . 7 + 3.1. Calendar Server . . . . . . . . . . . . . . . . . . . . . 7 + 3.2. Recurrence and the Data Model . . . . . . . . . . . . . . 8 + 4. Calendar Resources . . . . . . . . . . . . . . . . . . . . . . 9 + 4.1. Calendar Object Resources . . . . . . . . . . . . . . . . 9 + 4.2. Calendar Collection . . . . . . . . . . . . . . . . . . . 10 + 5. Calendar Access Feature . . . . . . . . . . . . . . . . . . . 11 + 5.1. Calendar Access Support . . . . . . . . . . . . . . . . . 11 + 5.1.1. Example: Using OPTIONS for the Discovery of + Calendar Access Support . . . . . . . . . . . . . . . 12 + 5.2. Calendar Collection Properties . . . . . . . . . . . . . . 12 + 5.2.1. CALDAV:calendar-description Property . . . . . . . . . 12 + 5.2.2. CALDAV:calendar-timezone Property . . . . . . . . . . 13 + 5.2.3. CALDAV:supported-calendar-component-set Property . . . 14 + 5.2.4. CALDAV:supported-calendar-data Property . . . . . . . 15 + 5.2.5. CALDAV:max-resource-size Property . . . . . . . . . . 16 + 5.2.6. CALDAV:min-date-time Property . . . . . . . . . . . . 17 + 5.2.7. CALDAV:max-date-time Property . . . . . . . . . . . . 18 + 5.2.8. CALDAV:max-instances Property . . . . . . . . . . . . 19 + 5.2.9. CALDAV:max-attendees-per-instance Property . . . . . . 19 + 5.2.10. Additional Precondition for PROPPATCH . . . . . . . . 20 + 5.3. Creating Resources . . . . . . . . . . . . . . . . . . . . 20 + 5.3.1. MKCALENDAR Method . . . . . . . . . . . . . . . . . . 20 + 5.3.1.1. Status Codes . . . . . . . . . . . . . . . . . . . 22 + 5.3.1.2. Example: Successful MKCALENDAR Request . . . . . . 23 + 5.3.2. Creating Calendar Object Resources . . . . . . . . . . 25 + 5.3.2.1. Additional Preconditions for PUT, COPY, and + MOVE . . . . . . . . . . . . . . . . . . . . . . . 26 + 5.3.3. Non-Standard Components, Properties, and Parameters . 28 + 5.3.4. Calendar Object Resource Entity Tag . . . . . . . . . 28 + 6. Calendaring Access Control . . . . . . . . . . . . . . . . . . 29 + 6.1. Calendaring Privilege . . . . . . . . . . . . . . . . . . 29 + 6.1.1. CALDAV:read-free-busy Privilege . . . . . . . . . . . 29 + 6.2. Additional Principal Property . . . . . . . . . . . . . . 30 + 6.2.1. CALDAV:calendar-home-set Property . . . . . . . . . . 30 + 7. Calendaring Reports . . . . . . . . . . . . . . . . . . . . . 31 + 7.1. REPORT Method . . . . . . . . . . . . . . . . . . . . . . 31 + 7.2. Ordinary Collections . . . . . . . . . . . . . . . . . . . 31 + 7.3. Date and Floating Time . . . . . . . . . . . . . . . . . . 32 + 7.4. Time Range Filtering . . . . . . . . . . . . . . . . . . . 32 + 7.5. Searching Text: Collations . . . . . . . . . . . . . . . . 33 + + + +Daboo, et al. Standards Track [Page 2] + +RFC 4791 CalDAV March 2007 + + + 7.5.1. CALDAV:supported-collation-set Property . . . . . . . 34 + 7.6. Partial Retrieval . . . . . . . . . . . . . . . . . . . . 34 + 7.7. Non-Standard Components, Properties, and Parameters . . . 35 + 7.8. CALDAV:calendar-query REPORT . . . . . . . . . . . . . . . 36 + 7.8.1. Example: Partial Retrieval of Events by Time Range . . 38 + 7.8.2. Example: Partial Retrieval of Recurring Events . . . . 42 + 7.8.3. Example: Expanded Retrieval of Recurring Events . . . 45 + 7.8.4. Example: Partial Retrieval of Stored Free Busy + Components . . . . . . . . . . . . . . . . . . . . . . 48 + 7.8.5. Example: Retrieval of To-Dos by Alarm Time Range . . . 50 + 7.8.6. Example: Retrieval of Event by UID . . . . . . . . . . 51 + 7.8.7. Example: Retrieval of Events by PARTSTAT . . . . . . . 53 + 7.8.8. Example: Retrieval of Events Only . . . . . . . . . . 55 + 7.8.9. Example: Retrieval of All Pending To-Dos . . . . . . . 59 + 7.8.10. Example: Attempt to Query Unsupported Property . . . . 62 + 7.9. CALDAV:calendar-multiget REPORT . . . . . . . . . . . . . 63 + 7.9.1. Example: Successful CALDAV:calendar-multiget REPORT . 64 + 7.10. CALDAV:free-busy-query REPORT . . . . . . . . . . . . . . 66 + 7.10.1. Example: Successful CALDAV:free-busy-query REPORT . . 68 + 8. Guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . 69 + 8.1. Client-to-Client Interoperability . . . . . . . . . . . . 69 + 8.2. Synchronization Operations . . . . . . . . . . . . . . . . 69 + 8.2.1. Use of Reports . . . . . . . . . . . . . . . . . . . . 69 + 8.2.1.1. Restrict the Time Range . . . . . . . . . . . . . 69 + 8.2.1.2. Synchronize by Time Range . . . . . . . . . . . . 70 + 8.2.1.3. Synchronization Process . . . . . . . . . . . . . 70 + 8.2.2. Restrict the Properties Returned . . . . . . . . . . . 72 + 8.3. Use of Locking . . . . . . . . . . . . . . . . . . . . . . 72 + 8.4. Finding Calendars . . . . . . . . . . . . . . . . . . . . 72 + 8.5. Storing and Using Attachments . . . . . . . . . . . . . . 74 + 8.5.1. Inline Attachments . . . . . . . . . . . . . . . . . . 74 + 8.5.2. External Attachments . . . . . . . . . . . . . . . . . 75 + 8.6. Storing and Using Alarms . . . . . . . . . . . . . . . . . 76 + 9. XML Element Definitions . . . . . . . . . . . . . . . . . . . 77 + 9.1. CALDAV:calendar XML Element . . . . . . . . . . . . . . . 77 + 9.2. CALDAV:mkcalendar XML Element . . . . . . . . . . . . . . 77 + 9.3. CALDAV:mkcalendar-response XML Element . . . . . . . . . . 78 + 9.4. CALDAV:supported-collation XML Element . . . . . . . . . . 78 + 9.5. CALDAV:calendar-query XML Element . . . . . . . . . . . . 78 + 9.6. CALDAV:calendar-data XML Element . . . . . . . . . . . . . 79 + 9.6.1. CALDAV:comp XML Element . . . . . . . . . . . . . . . 80 + 9.6.2. CALDAV:allcomp XML Element . . . . . . . . . . . . . . 81 + 9.6.3. CALDAV:allprop XML Element . . . . . . . . . . . . . . 81 + 9.6.4. CALDAV:prop XML Element . . . . . . . . . . . . . . . 82 + 9.6.5. CALDAV:expand XML Element . . . . . . . . . . . . . . 82 + 9.6.6. CALDAV:limit-recurrence-set XML Element . . . . . . . 83 + 9.6.7. CALDAV:limit-freebusy-set XML Element . . . . . . . . 84 + 9.7. CALDAV:filter XML Element . . . . . . . . . . . . . . . . 85 + + + +Daboo, et al. Standards Track [Page 3] + +RFC 4791 CalDAV March 2007 + + + 9.7.1. CALDAV:comp-filter XML Element . . . . . . . . . . . . 85 + 9.7.2. CALDAV:prop-filter XML Element . . . . . . . . . . . . 86 + 9.7.3. CALDAV:param-filter XML Element . . . . . . . . . . . 87 + 9.7.4. CALDAV:is-not-defined XML Element . . . . . . . . . . 88 + 9.7.5. CALDAV:text-match XML Element . . . . . . . . . . . . 88 + 9.8. CALDAV:timezone XML Element . . . . . . . . . . . . . . . 89 + 9.9. CALDAV:time-range XML Element . . . . . . . . . . . . . . 90 + 9.10. CALDAV:calendar-multiget XML Element . . . . . . . . . . . 94 + 9.11. CALDAV:free-busy-query XML Element . . . . . . . . . . . . 95 + 10. Internationalization Considerations . . . . . . . . . . . . . 95 + 11. Security Considerations . . . . . . . . . . . . . . . . . . . 95 + 12. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 96 + 12.1. Namespace Registration . . . . . . . . . . . . . . . . . . 96 + 13. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 96 + 14. References . . . . . . . . . . . . . . . . . . . . . . . . . . 97 + 14.1. Normative References . . . . . . . . . . . . . . . . . . . 97 + 14.2. Informative References . . . . . . . . . . . . . . . . . . 98 + Appendix A. CalDAV Method Privilege Table (Normative) . . . . . . 99 + Appendix B. Calendar Collections Used in the Examples . . . . . . 99 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 4] + +RFC 4791 CalDAV March 2007 + + +1. Introduction + + The concept of using HTTP [RFC2616] and WebDAV [RFC2518] as a basis + for a calendar access protocol is by no means a new concept: it was + discussed in the IETF CALSCH working group as early as 1997 or 1998. + Several companies have implemented calendar access protocols using + HTTP to upload and download iCalendar [RFC2445] objects, and using + WebDAV to get listings of resources. However, those implementations + do not interoperate because there are many small and big decisions to + be made in how to model calendaring data as WebDAV resources, as well + as how to implement required features that aren't already part of + WebDAV. This document proposes a way to model calendar data in + WebDAV, with additional features to make an interoperable calendar + access protocol. + +1.1. Notational Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + The term "protected" is used in the Conformance field of property + definitions as defined in Section 1.4.2 of [RFC3253]. + + When XML element types in the namespaces "DAV:" and + "urn:ietf:params:xml:ns:caldav" are referenced in this document + outside of the context of an XML fragment, the string "DAV:" and + "CALDAV:" will be prefixed to the element type names, respectively. + +1.2. XML Namespaces and Processing + + Definitions of XML elements in this document use XML element type + declarations (as found in XML Document Type Declarations), described + in Section 3.2 of [W3C.REC-xml-20060816]. + + The namespace "urn:ietf:params:xml:ns:caldav" is reserved for the XML + elements defined in this specification, its revisions, and related + CalDAV specifications. XML elements defined by individual + implementations MUST NOT use the "urn:ietf:params:xml:ns:caldav" + namespace, and instead should use a namespace that they control. + + The XML declarations used in this document do not include namespace + information. Thus, implementers must not use these declarations as + the only way to create valid CalDAV properties or to validate CalDAV + XML element types. Some of the declarations refer to XML elements + defined by WebDAV [RFC2518], which use the "DAV:" namespace. + Wherever such XML elements appear, they are explicitly prefixed with + "DAV:" to avoid confusion. + + + +Daboo, et al. Standards Track [Page 5] + +RFC 4791 CalDAV March 2007 + + + Also note that some CalDAV XML element names are identical to WebDAV + XML element names, though their namespace differs. Care must be + taken not to confuse the two sets of names. + + Processing of XML by CalDAV clients and servers MUST follow the rules + described in [RFC2518]; in particular, Section 14, and Appendix 3 of + that specification. + +1.3. Method Preconditions and Postconditions + + A "precondition" of a method describes the state of the server that + must be true for that method to be performed. A "postcondition" of a + method describes the state of the server that must be true after that + method has been completed. If a method precondition or postcondition + for a request is not satisfied, the response status of the request + MUST either be 403 (Forbidden), if the request should not be repeated + because it will always fail, or 409 (Conflict), if it is expected + that the user might be able to resolve the conflict and resubmit the + request. + + In order to allow better client handling of 403 and 409 responses, a + distinct XML element type is associated with each method precondition + and postcondition of a request. When a particular precondition is + not satisfied or a particular postcondition cannot be achieved, the + appropriate XML element MUST be returned as the child of a top-level + DAV:error element in the response body, unless otherwise negotiated + by the request. + +2. Requirements Overview + + This section lists what functionality is required of a CalDAV server. + To advertise support for CalDAV, a server: + + o MUST support iCalendar [RFC2445] as a media type for the calendar + object resource format; + + o MUST support WebDAV Class 1 [RFC2518] (note that [rfc2518bis] + describes clarifications to [RFC2518] that aid interoperability); + + o MUST support WebDAV ACL [RFC3744] with the additional privilege + defined in Section 6.1 of this document; + + o MUST support transport over TLS [RFC2246] as defined in [RFC2818] + (note that [RFC2246] has been obsoleted by [RFC4346]); + + o MUST support ETags [RFC2616] with additional requirements + specified in Section 5.3.4 of this document; + + + + +Daboo, et al. Standards Track [Page 6] + +RFC 4791 CalDAV March 2007 + + + o MUST support all calendaring reports defined in Section 7 of this + document; and + + o MUST advertise support on all calendar collections and calendar + object resources for the calendaring reports in the DAV:supported- + report-set property, as defined in Versioning Extensions to WebDAV + [RFC3253]. + + In addition, a server: + + o SHOULD support the MKCALENDAR method defined in Section 5.3.1 of + this document. + +3. Calendaring Data Model + + One of the features that has made WebDAV a successful protocol is its + firm data model. This makes it a useful framework for other + applications such as calendaring. This specification follows the + same pattern by developing all features based on a well-described + data model. + + As a brief overview, a CalDAV calendar is modeled as a WebDAV + collection with a defined structure; each calendar collection + contains a number of resources representing calendar objects as its + direct child resource. Each resource representing a calendar object + (event, to-do, journal entry, or other calendar components) is called + a "calendar object resource". Each calendar object resource and each + calendar collection can be individually locked and have individual + WebDAV properties. Requirements derived from this model are provided + in Section 4.1 and Section 4.2. + +3.1. Calendar Server + + A CalDAV server is a calendaring-aware engine combined with a WebDAV + repository. A WebDAV repository is a set of WebDAV collections, + containing other WebDAV resources, within a unified URL namespace. + For example, the repository "http://www.example.com/webdav/" may + contain WebDAV collections and resources, all of which have URLs + beginning with "http://www.example.com/webdav/". Note that the root + URL, "http://www.example.com/", may not itself be a WebDAV repository + (for example, if the WebDAV support is implemented through a servlet + or other Web server extension). + + A WebDAV repository MAY include calendar data in some parts of its + URL namespace, and non-calendaring data in other parts. + + A WebDAV repository can advertise itself as a CalDAV server if it + supports the functionality defined in this specification at any point + + + +Daboo, et al. Standards Track [Page 7] + +RFC 4791 CalDAV March 2007 + + + within the root of the repository. That might mean that calendaring + data is spread throughout the repository and mixed with non-calendar + data in nearby collections (e.g., calendar data may be found in + /home/lisa/calendars/ as well as in /home/bernard/calendars/, and + non-calendar data in /home/lisa/contacts/). Or, it might mean that + calendar data can be found only in certain sections of the repository + (e.g., /calendar/). Calendaring features are only required in the + repository sections that are or contain calendar object resources. + Therefore, a repository confining calendar data to the /calendar/ + collection would only need to support the CalDAV required features + within that collection. + + The CalDAV server or repository is the canonical location for + calendar data and state information. Clients may submit requests to + change data or download data. Clients may store calendar objects + offline and attempt to synchronize at a later time. However, clients + MUST be prepared for calendar data on the server to change between + the time of last synchronization and when attempting an update, as + calendar collections may be shared and accessible via multiple + clients. Entity tags and other features make this possible. + +3.2. Recurrence and the Data Model + + Recurrence is an important part of the data model because it governs + how many resources are expected to exist. This specification models + a recurring calendar component and its recurrence exceptions as a + single resource. In this model, recurrence rules, recurrence dates, + exception rules, and exception dates are all part of the data in a + single calendar object resource. This model avoids problems of + limiting how many recurrence instances to store in the repository, + how to keep recurrence instances in sync with the recurring calendar + component, and how to link recurrence exceptions with the recurring + calendar component. It also results in less data to synchronize + between client and server, and makes it easier to make changes to all + recurrence instances or to a recurrence rule. It makes it easier to + create a recurring calendar component and to delete all recurrence + instances. + + Clients are not forced to retrieve information about all recurrence + instances of a recurring component. The CALDAV:calendar-query and + CALDAV:calendar-multiget reports defined in this document allow + clients to retrieve only recurrence instances that overlap a given + time range. + + + + + + + + +Daboo, et al. Standards Track [Page 8] + +RFC 4791 CalDAV March 2007 + + +4. Calendar Resources + +4.1. Calendar Object Resources + + Calendar object resources contained in calendar collections MUST NOT + contain more than one type of calendar component (e.g., VEVENT, + VTODO, VJOURNAL, VFREEBUSY, etc.) with the exception of VTIMEZONE + components, which MUST be specified for each unique TZID parameter + value specified in the iCalendar object. For instance, a calendar + object resource can contain one VEVENT component and one VTIMEZONE + component, but it cannot contain one VEVENT component and one VTODO + component. Instead, the VEVENT and VTODO components would have to be + stored in separate calendar object resources in the same collection. + + Calendar object resources contained in calendar collections MUST NOT + specify the iCalendar METHOD property. + + The UID property value of the calendar components contained in a + calendar object resource MUST be unique in the scope of the calendar + collection in which they are stored. + + Calendar components in a calendar collection that have different UID + property values MUST be stored in separate calendar object resources. + + Calendar components with the same UID property value, in a given + calendar collection, MUST be contained in the same calendar object + resource. This ensures that all components in a recurrence "set" are + contained in the same calendar object resource. It is possible for a + calendar object resource to just contain components that represent + "overridden" instances (ones that modify the behavior of a regular + instance, and thus include a RECURRENCE-ID property) without also + including the "master" recurring component (the one that defines the + recurrence "set" and does not contain any RECURRENCE-ID property). + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 9] + +RFC 4791 CalDAV March 2007 + + + For example, given the following iCalendar object: + + BEGIN:VCALENDAR + PRODID:-//Example Corp.//CalDAV Client//EN + VERSION:2.0 + BEGIN:VEVENT + UID:1@example.com + SUMMARY:One-off Meeting + DTSTAMP:20041210T183904Z + DTSTART:20041207T120000Z + DTEND:20041207T130000Z + END:VEVENT + BEGIN:VEVENT + UID:2@example.com + SUMMARY:Weekly Meeting + DTSTAMP:20041210T183838Z + DTSTART:20041206T120000Z + DTEND:20041206T130000Z + RRULE:FREQ=WEEKLY + END:VEVENT + BEGIN:VEVENT + UID:2@example.com + SUMMARY:Weekly Meeting + RECURRENCE-ID:20041213T120000Z + DTSTAMP:20041210T183838Z + DTSTART:20041213T130000Z + DTEND:20041213T140000Z + END:VEVENT + END:VCALENDAR + + The VEVENT component with the UID value "1@example.com" would be + stored in its own calendar object resource. The two VEVENT + components with the UID value "2@example.com", which represent a + recurring event where one recurrence instance has been overridden, + would be stored in the same calendar object resource. + +4.2. Calendar Collection + + A calendar collection contains calendar object resources that + represent calendar components within a calendar. A calendar + collection is manifested to clients as a WebDAV resource collection + identified by a URL. A calendar collection MUST report the DAV: + collection and CALDAV:calendar XML elements in the value of the DAV: + resourcetype property. The element type declaration for CALDAV: + calendar is: + + + + + + +Daboo, et al. Standards Track [Page 10] + +RFC 4791 CalDAV March 2007 + + + A calendar collection can be created through provisioning (i.e., + automatically created when a user's account is provisioned), or it + can be created with the MKCALENDAR method (see Section 5.3.1). This + method can be useful for a user to create additional calendars (e.g., + soccer schedule) or for users to share a calendar (e.g., team events + or conference rooms). However, note that this document doesn't + define the purpose of extra calendar collections. Users must rely on + non-standard cues to find out what a calendar collection is for, or + use the CALDAV:calendar-description property defined in Section 5.2.1 + to provide such a cue. + + The following restrictions are applied to the resources within a + calendar collection: + + a. Calendar collections MUST only contain calendar object resources + and collections that are not calendar collections, i.e., the only + "top-level" non-collection resources allowed in a calendar + collection are calendar object resources. This ensures that + calendar clients do not have to deal with non-calendar data in a + calendar collection, though they do have to distinguish between + calendar object resources and collections when using standard + WebDAV techniques to examine the contents of a collection. + + b. Collections contained in calendar collections MUST NOT contain + calendar collections at any depth, i.e., "nesting" of calendar + collections within other calendar collections at any depth is not + allowed. This specification does not define how collections + contained in a calendar collection are used or how they relate to + any calendar object resources contained in the calendar + collection. + + Multiple calendar collections MAY be children of the same collection. + +5. Calendar Access Feature + +5.1. Calendar Access Support + + A server supporting the features described in this document MUST + include "calendar-access" as a field in the DAV response header from + an OPTIONS request on any resource that supports any calendar + properties, reports, method, or privilege. A value of "calendar- + access" in the DAV response header MUST indicate that the server + supports all MUST level requirements specified in this document. + + + + + + + + +Daboo, et al. Standards Track [Page 11] + +RFC 4791 CalDAV March 2007 + + +5.1.1. Example: Using OPTIONS for the Discovery of Calendar Access + Support + + >> Request << + + OPTIONS /home/bernard/calendars/ HTTP/1.1 + Host: cal.example.com + + >> Response << + + HTTP/1.1 200 OK + Allow: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE + Allow: PROPFIND, PROPPATCH, LOCK, UNLOCK, REPORT, ACL + DAV: 1, 2, access-control, calendar-access + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Length: 0 + + In this example, the OPTIONS method returns the value "calendar- + access" in the DAV response header to indicate that the collection + "/home/bernard/calendars/" supports the properties, reports, method, + or privilege defined in this specification. + +5.2. Calendar Collection Properties + + This section defines properties for calendar collections. + +5.2.1. CALDAV:calendar-description Property + + Name: calendar-description + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Provides a human-readable description of the calendar + collection. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MAY be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). An xml:lang attribute indicating the human + language of the description SHOULD be set for this property by + clients or through server provisioning. Servers MUST return any + xml:lang attribute if set for the property. + + Description: If present, the property contains a description of the + calendar collection that is suitable for presentation to a user. + If not present, the client should assume no description for the + calendar collection. + + + + +Daboo, et al. Standards Track [Page 12] + +RFC 4791 CalDAV March 2007 + + + Definition: + + + PCDATA value: string + + Example: + + Calendrier de Mathilde Desruisseaux + +5.2.2. CALDAV:calendar-timezone Property + + Name: calendar-timezone + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a time zone on a calendar collection. + + Conformance: This property SHOULD be defined on all calendar + collections. If defined, it SHOULD NOT be returned by a PROPFIND + DAV:allprop request (as defined in Section 12.14.1 of [RFC2518]). + + Description: The CALDAV:calendar-timezone property is used to + specify the time zone the server should rely on to resolve "date" + values and "date with local time" values (i.e., floating time) to + "date with UTC time" values. The server will require this + information to determine if a calendar component scheduled with + "date" values or "date with local time" values overlaps a CALDAV: + time-range specified in a CALDAV:calendar-query REPORT. The + server will also require this information to compute the proper + FREEBUSY time period as "date with UTC time" in the VFREEBUSY + component returned in a response to a CALDAV:free-busy-query + REPORT request that takes into account calendar components + scheduled with "date" values or "date with local time" values. In + the absence of this property, the server MAY rely on the time zone + of their choice. + + Note: The iCalendar data embedded within the CALDAV:calendar- + timezone XML element MUST follow the standard XML character data + encoding rules, including use of <, >, & etc. entity + encoding or the use of a construct. In the + later case, the iCalendar data cannot contain the character + sequence "]]>", which is the end delimiter for the CDATA section. + + + + + + + +Daboo, et al. Standards Track [Page 13] + +RFC 4791 CalDAV March 2007 + + + Definition: + + + PCDATA value: an iCalendar object with exactly one VTIMEZONE + component. + + Example: + + BEGIN:VCALENDAR + PRODID:-//Example Corp.//CalDAV Client//EN + VERSION:2.0 + BEGIN:VTIMEZONE + TZID:US-Eastern + LAST-MODIFIED:19870101T000000Z + BEGIN:STANDARD + DTSTART:19671029T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + TZNAME:Eastern Standard Time (US & Canada) + END:STANDARD + BEGIN:DAYLIGHT + DTSTART:19870405T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + TZNAME:Eastern Daylight Time (US & Canada) + END:DAYLIGHT + END:VTIMEZONE + END:VCALENDAR + + +5.2.3. CALDAV:supported-calendar-component-set Property + + Name: supported-calendar-component-set + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies the calendar component types (e.g., VEVENT, + VTODO, etc.) that calendar object resources can contain in the + calendar collection. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MUST be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + + + +Daboo, et al. Standards Track [Page 14] + +RFC 4791 CalDAV March 2007 + + + Description: The CALDAV:supported-calendar-component-set property is + used to specify restrictions on the calendar component types that + calendar object resources may contain in a calendar collection. + Any attempt by the client to store calendar object resources with + component types not listed in this property, if it exists, MUST + result in an error, with the CALDAV:supported-calendar-component + precondition (Section 5.3.2.1) being violated. Since this + property is protected, it cannot be changed by clients using a + PROPPATCH request. However, clients can initialize the value of + this property when creating a new calendar collection with + MKCALENDAR. The empty-element tag MUST + only be specified if support for calendar object resources that + only contain VTIMEZONE components is provided or desired. Support + for VTIMEZONE components in calendar object resources that contain + VEVENT or VTODO components is always assumed. In the absence of + this property, the server MUST accept all component types, and the + client can assume that all component types are accepted. + + Definition: + + + + Example: + + + + + + +5.2.4. CALDAV:supported-calendar-data Property + + Name: supported-calendar-data + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies what media types are allowed for calendar object + resources in a calendar collection. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MUST be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + Description: The CALDAV:supported-calendar-data property is used to + specify the media type supported for the calendar object resources + contained in a given calendar collection (e.g., iCalendar version + 2.0). Any attempt by the client to store calendar object + + + +Daboo, et al. Standards Track [Page 15] + +RFC 4791 CalDAV March 2007 + + + resources with a media type not listed in this property MUST + result in an error, with the CALDAV:supported-calendar-data + precondition (Section 5.3.2.1) being violated. In the absence of + this property, the server MUST only accept data with the media + type "text/calendar" and iCalendar version 2.0, and clients can + assume that the server will only accept this data. + + Definition: + + + + Example: + + + + + +5.2.5. CALDAV:max-resource-size Property + + Name: max-resource-size + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Provides a numeric value indicating the maximum size of a + resource in octets that the server is willing to accept when a + calendar object resource is stored in a calendar collection. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MUST be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + Description: The CALDAV:max-resource-size is used to specify a + numeric value that represents the maximum size in octets that the + server is willing to accept when a calendar object resource is + stored in a calendar collection. Any attempt to store a calendar + object resource exceeding this size MUST result in an error, with + the CALDAV:max-resource-size precondition (Section 5.3.2.1) being + violated. In the absence of this property, the client can assume + that the server will allow storing a resource of any reasonable + size. + + Definition: + + + PCDATA value: a numeric value (positive integer) + + + + +Daboo, et al. Standards Track [Page 16] + +RFC 4791 CalDAV March 2007 + + + Example: + + 102400 + +5.2.6. CALDAV:min-date-time Property + + Name: min-date-time + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Provides a DATE-TIME value indicating the earliest date and + time (in UTC) that the server is willing to accept for any DATE or + DATE-TIME value in a calendar object resource stored in a calendar + collection. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MUST be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + Description: The CALDAV:min-date-time is used to specify an + iCalendar DATE-TIME value in UTC that indicates the earliest + inclusive date that the server is willing to accept for any + explicit DATE or DATE-TIME value in a calendar object resource + stored in a calendar collection. Any attempt to store a calendar + object resource using a DATE or DATE-TIME value earlier than this + value MUST result in an error, with the CALDAV:min-date-time + precondition (Section 5.3.2.1) being violated. Note that servers + MUST accept recurring components that specify instances beyond + this limit, provided none of those instances have been overridden. + In that case, the server MAY simply ignore those instances outside + of the acceptable range when processing reports on the calendar + object resource. In the absence of this property, the client can + assume any valid iCalendar date may be used at least up to the + CALDAV:max-date-time value, if that is defined. + + Definition: + + + PCDATA value: an iCalendar format DATE-TIME value in UTC + + Example: + + 19000101T000000Z + + + + + +Daboo, et al. Standards Track [Page 17] + +RFC 4791 CalDAV March 2007 + + +5.2.7. CALDAV:max-date-time Property + + Name: max-date-time + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Provides a DATE-TIME value indicating the latest date and + time (in UTC) that the server is willing to accept for any DATE or + DATE-TIME value in a calendar object resource stored in a calendar + collection. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MUST be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + Description: The CALDAV:max-date-time is used to specify an + iCalendar DATE-TIME value in UTC that indicates the inclusive + latest date that the server is willing to accept for any date or + time value in a calendar object resource stored in a calendar + collection. Any attempt to store a calendar object resource using + a DATE or DATE-TIME value later than this value MUST result in an + error, with the CALDAV:max-date-time precondition + (Section 5.3.2.1) being violated. Note that servers MUST accept + recurring components that specify instances beyond this limit, + provided none of those instances have been overridden. In that + case, the server MAY simply ignore those instances outside of the + acceptable range when processing reports on the calendar object + resource. In the absence of this property, the client can assume + any valid iCalendar date may be used at least down to the CALDAV: + min-date-time value, if that is defined. + + Definition: + + + PCDATA value: an iCalendar format DATE-TIME value in UTC + + Example: + + 20491231T235959Z + + + + + + + + + + +Daboo, et al. Standards Track [Page 18] + +RFC 4791 CalDAV March 2007 + + +5.2.8. CALDAV:max-instances Property + + Name: max-instances + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Provides a numeric value indicating the maximum number of + recurrence instances that a calendar object resource stored in a + calendar collection can generate. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MUST be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + Description: The CALDAV:max-instances is used to specify a numeric + value that indicates the maximum number of recurrence instances + that a calendar object resource stored in a calendar collection + can generate. Any attempt to store a calendar object resource + with a recurrence pattern that generates more instances than this + value MUST result in an error, with the CALDAV:max-instances + precondition (Section 5.3.2.1) being violated. In the absence of + this property, the client can assume that the server has no limits + on the number of recurrence instances it can handle or expand. + + Definition: + + + PCDATA value: a numeric value (integer greater than zero) + + Example: + + 100 + +5.2.9. CALDAV:max-attendees-per-instance Property + + Name: max-attendees-per-instance + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Provides a numeric value indicating the maximum number of + ATTENDEE properties in any instance of a calendar object resource + stored in a calendar collection. + + Conformance: This property MAY be defined on any calendar + collection. If defined, it MUST be protected and SHOULD NOT be + + + + +Daboo, et al. Standards Track [Page 19] + +RFC 4791 CalDAV March 2007 + + + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + Description: The CALDAV:max-attendees-per-instance is used to + specify a numeric value that indicates the maximum number of + iCalendar ATTENDEE properties on any one instance of a calendar + object resource stored in a calendar collection. Any attempt to + store a calendar object resource with more ATTENDEE properties per + instance than this value MUST result in an error, with the CALDAV: + max-attendees-per-instance precondition (Section 5.3.2.1) being + violated. In the absence of this property, the client can assume + that the server can handle any number of ATTENDEE properties in a + calendar component. + + Definition: + + + PCDATA value: a numeric value (integer greater than zero) + + Example: + + 25 + +5.2.10. Additional Precondition for PROPPATCH + + This specification requires an additional Precondition for the + PROPPATCH method. The precondition is: + + (CALDAV:valid-calendar-data): The time zone specified in CALDAV: + calendar-timezone property MUST be a valid iCalendar object + containing a single valid VTIMEZONE component. + +5.3. Creating Resources + + Calendar collections and calendar object resources may be created by + either a CalDAV client or by the CalDAV server. This specification + defines restrictions and a data model that both clients and servers + MUST adhere to when manipulating such calendar data. + +5.3.1. MKCALENDAR Method + + An HTTP request using the MKCALENDAR method creates a new calendar + collection resource. A server MAY restrict calendar collection + creation to particular collections. + + + + + +Daboo, et al. Standards Track [Page 20] + +RFC 4791 CalDAV March 2007 + + + Support for MKCALENDAR on the server is only RECOMMENDED and not + REQUIRED because some calendar stores only support one calendar per + user (or principal), and those are typically pre-created for each + account. However, servers and clients are strongly encouraged to + support MKCALENDAR whenever possible to allow users to create + multiple calendar collections to help organize their data better. + + Clients SHOULD use the DAV:displayname property for a human-readable + name of the calendar. Clients can either specify the value of the + DAV:displayname property in the request body of the MKCALENDAR + request, or alternatively issue a PROPPATCH request to change the + DAV:displayname property to the appropriate value immediately after + issuing the MKCALENDAR request. Clients SHOULD NOT set the DAV: + displayname property to be the same as any other calendar collection + at the same URI "level". When displaying calendar collections to + users, clients SHOULD check the DAV:displayname property and use that + value as the name of the calendar. In the event that the DAV: + displayname property is empty, the client MAY use the last part of + the calendar collection URI as the name; however, that path segment + may be "opaque" and not represent any meaningful human-readable text. + + If a MKCALENDAR request fails, the server state preceding the request + MUST be restored. + + Marshalling: + If a request body is included, it MUST be a CALDAV:mkcalendar XML + element. Instruction processing MUST occur in the order + instructions are received (i.e., from top to bottom). + Instructions MUST either all be executed or none executed. Thus, + if any error occurs during processing, all executed instructions + MUST be undone and a proper error result returned. Instruction + processing details can be found in the definition of the DAV:set + instruction in Section 12.13.2 of [RFC2518]. + + + + If a response body for a successful request is included, it MUST + be a CALDAV:mkcalendar-response XML element. + + + + The response MUST include a Cache-Control:no-cache header. + + Preconditions: + + (DAV:resource-must-be-null): A resource MUST NOT exist at the + Request-URI; + + + + +Daboo, et al. Standards Track [Page 21] + +RFC 4791 CalDAV March 2007 + + + (CALDAV:calendar-collection-location-ok): The Request-URI MUST + identify a location where a calendar collection can be created; + + (CALDAV:valid-calendar-data): The time zone specified in the + CALDAV:calendar-timezone property MUST be a valid iCalendar object + containing a single valid VTIMEZONE component; + + (DAV:needs-privilege): The DAV:bind privilege MUST be granted to + the current user on the parent collection of the Request-URI. + + Postconditions: + + (CALDAV:initialize-calendar-collection): A new calendar collection + exists at the Request-URI. The DAV:resourcetype of the calendar + collection MUST contain both DAV:collection and CALDAV:calendar + XML elements. + +5.3.1.1. Status Codes + + The following are examples of response codes one would expect to get + in a response to a MKCALENDAR request. Note that this list is by no + means exhaustive. + + 201 (Created) - The calendar collection resource was created in + its entirety; + + 207 (Multi-Status) - The calendar collection resource was not + created since one or more DAV:set instructions specified in the + request body could not be processed successfully. The following + are examples of response codes one would expect to be used in a + 207 (Multi-Status) response in this situation: + + 403 (Forbidden) - The client, for reasons the server chooses + not to specify, cannot alter one of the properties; + + 409 (Conflict) - The client has provided a value whose + semantics are not appropriate for the property. This includes + trying to set read-only properties; + + 424 (Failed Dependency) - The DAV:set instruction on the + specified resource would have succeeded if it were not for the + failure of another DAV:set instruction specified in the request + body; + + 423 (Locked) - The specified resource is locked and the client + either is not a lock owner or the lock type requires a lock + token to be submitted and the client did not submit it; and + + + + +Daboo, et al. Standards Track [Page 22] + +RFC 4791 CalDAV March 2007 + + + 507 (Insufficient Storage) - The server did not have sufficient + space to record the property; + + 403 (Forbidden) - This indicates at least one of two conditions: + 1) the server does not allow the creation of calendar collections + at the given location in its namespace, or 2) the parent + collection of the Request-URI exists but cannot accept members; + + 409 (Conflict) - A collection cannot be made at the Request-URI + until one or more intermediate collections have been created; + + 415 (Unsupported Media Type) - The server does not support the + request type of the body; and + + 507 (Insufficient Storage) - The resource does not have sufficient + space to record the state of the resource after the execution of + this method. + +5.3.1.2. Example: Successful MKCALENDAR Request + + This example creates a calendar collection called /home/lisa/ + calendars/events/ on the server cal.example.com with specific values + for the properties DAV:displayname, CALDAV:calendar-description, + CALDAV:supported-calendar-component-set, and CALDAV:calendar- + timezone. + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 23] + +RFC 4791 CalDAV March 2007 + + + >> Request << + + MKCALENDAR /home/lisa/calendars/events/ HTTP/1.1 + Host: cal.example.com + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + Lisa's Events + Calendar restricted to events. + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 24] + +RFC 4791 CalDAV March 2007 + + + >> Response << + + HTTP/1.1 201 Created + Cache-Control: no-cache + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Length: 0 + +5.3.2. Creating Calendar Object Resources + + Clients populate calendar collections with calendar object resources. + The URL for each calendar object resource is entirely arbitrary and + does not need to bear a specific relationship to the calendar object + resource's iCalendar properties or other metadata. New calendar + object resources MUST be created with a PUT request targeted at an + unmapped URI. A PUT request targeted at a mapped URI updates an + existing calendar object resource. + + When servers create new resources, it's not hard for the server to + choose an unmapped URI. It's slightly tougher for clients, because a + client might not want to examine all resources in the collection and + might not want to lock the entire collection to ensure that a new + resource isn't created with a name collision. However, there is an + HTTP feature to mitigate this. If the client intends to create a new + non-collection resource, such as a new VEVENT, the client SHOULD use + the HTTP request header "If-None-Match: *" on the PUT request. The + Request-URI on the PUT request MUST include the target collection, + where the resource is to be created, plus the name of the resource in + the last path segment. The "If-None-Match: *" request header ensures + that the client will not inadvertently overwrite an existing resource + if the last path segment turned out to already be used. + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 25] + +RFC 4791 CalDAV March 2007 + + + >> Request << + + PUT /home/lisa/calendars/events/qwue23489.ics HTTP/1.1 + If-None-Match: * + Host: cal.example.com + Content-Type: text/calendar + Content-Length: xxxx + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VEVENT + UID:20010712T182145Z-123401@example.com + DTSTAMP:20060712T182145Z + DTSTART:20060714T170000Z + DTEND:20060715T040000Z + SUMMARY:Bastille Day Party + END:VEVENT + END:VCALENDAR + + >> Response << + + HTTP/1.1 201 Created + Content-Length: 0 + Date: Sat, 11 Nov 2006 09:32:12 GMT + ETag: "123456789-000-111" + + The request to change an existing event is the same, but with a + specific ETag in the "If-Match" header, rather than the "If-None- + Match" header. + + As indicated in Section 3.10 of [RFC2445], the URL of calendar object + resources containing (an arbitrary set of) calendaring and scheduling + information may be suffixed by ".ics", and the URL of calendar object + resources containing free or busy time information may be suffixed by + ".ifb". + +5.3.2.1. Additional Preconditions for PUT, COPY, and MOVE + + This specification creates additional Preconditions for PUT, COPY, + and MOVE methods. These preconditions apply when a PUT operation of + a calendar object resource into a calendar collection occurs, or when + a COPY or MOVE operation of a calendar object resource into a + calendar collection occurs, or when a COPY or MOVE operation occurs + on a calendar collection. + + + + + + +Daboo, et al. Standards Track [Page 26] + +RFC 4791 CalDAV March 2007 + + + The new preconditions are: + + (CALDAV:supported-calendar-data): The resource submitted in the + PUT request, or targeted by a COPY or MOVE request, MUST be a + supported media type (i.e., iCalendar) for calendar object + resources; + + (CALDAV:valid-calendar-data): The resource submitted in the PUT + request, or targeted by a COPY or MOVE request, MUST be valid data + for the media type being specified (i.e., MUST contain valid + iCalendar data); + + (CALDAV:valid-calendar-object-resource): The resource submitted in + the PUT request, or targeted by a COPY or MOVE request, MUST obey + all restrictions specified in Section 4.1 (e.g., calendar object + resources MUST NOT contain more than one type of calendar + component, calendar object resources MUST NOT specify the + iCalendar METHOD property, etc.); + + (CALDAV:supported-calendar-component): The resource submitted in + the PUT request, or targeted by a COPY or MOVE request, MUST + contain a type of calendar component that is supported in the + targeted calendar collection; + + (CALDAV:no-uid-conflict): The resource submitted in the PUT + request, or targeted by a COPY or MOVE request, MUST NOT specify + an iCalendar UID property value already in use in the targeted + calendar collection or overwrite an existing calendar object + resource with one that has a different UID property value. + Servers SHOULD report the URL of the resource that is already + making use of the same UID property value in the DAV:href element; + + + + (CALDAV:calendar-collection-location-ok): In a COPY or MOVE + request, when the Request-URI is a calendar collection, the + Destination-URI MUST identify a location where a calendar + collection can be created; + + (CALDAV:max-resource-size): The resource submitted in the PUT + request, or targeted by a COPY or MOVE request, MUST have an octet + size less than or equal to the value of the CALDAV:max-resource- + size property value (Section 5.2.5) on the calendar collection + where the resource will be stored; + + (CALDAV:min-date-time): The resource submitted in the PUT request, + or targeted by a COPY or MOVE request, MUST have all of its + iCalendar DATE or DATE-TIME property values (for each recurring + + + +Daboo, et al. Standards Track [Page 27] + +RFC 4791 CalDAV March 2007 + + + instance) greater than or equal to the value of the CALDAV:min- + date-time property value (Section 5.2.6) on the calendar + collection where the resource will be stored; + + (CALDAV:max-date-time): The resource submitted in the PUT request, + or targeted by a COPY or MOVE request, MUST have all of its + iCalendar DATE or DATE-TIME property values (for each recurring + instance) less than the value of the CALDAV:max-date-time property + value (Section 5.2.7) on the calendar collection where the + resource will be stored; + + (CALDAV:max-instances): The resource submitted in the PUT request, + or targeted by a COPY or MOVE request, MUST generate a number of + recurring instances less than or equal to the value of the CALDAV: + max-instances property value (Section 5.2.8) on the calendar + collection where the resource will be stored; + + (CALDAV:max-attendees-per-instance): The resource submitted in the + PUT request, or targeted by a COPY or MOVE request, MUST have a + number of ATTENDEE properties on any one instance less than or + equal to the value of the CALDAV:max-attendees-per-instance + property value (Section 5.2.9) on the calendar collection where + the resource will be stored; + +5.3.3. Non-Standard Components, Properties, and Parameters + + iCalendar provides a "standard mechanism for doing non-standard + things". This extension support allows implementers to make use of + non-standard components, properties, and parameters whose names are + prefixed with the text "X-". + + Servers MUST support the use of non-standard components, properties, + and parameters in calendar object resources stored via the PUT + method. + + Servers may need to enforce rules for their own "private" components, + properties, or parameters, so servers MAY reject any attempt by the + client to change those or use values for those outside of any + restrictions the server may have. Servers SHOULD ensure that any + "private" components, properties, or parameters it uses follow the + convention of including a vendor id in the "X-" name, as described in + Section 4.2 of [RFC2445], e.g., "X-ABC-PRIVATE". + +5.3.4. Calendar Object Resource Entity Tag + + The DAV:getetag property MUST be defined and set to a strong entity + tag on all calendar object resources. + + + + +Daboo, et al. Standards Track [Page 28] + +RFC 4791 CalDAV March 2007 + + + A response to a GET request targeted at a calendar object resource + MUST contain an ETag response header field indicating the current + value of the strong entity tag of the calendar object resource. + + Servers SHOULD return a strong entity tag (ETag header) in a PUT + response when the stored calendar object resource is equivalent by + octet equality to the calendar object resource submitted in the body + of the PUT request. This allows clients to reliably use the returned + strong entity tag for data synchronization purposes. For instance, + the client can do a PROPFIND request on the stored calendar object + resource and have the DAV:getetag property returned, and compare that + value with the strong entity tag it received on the PUT response, and + know that if they are equal, then the calendar object resource on the + server has not been changed. + + In the case where the data stored by a server as a result of a PUT + request is not equivalent by octet equality to the submitted calendar + object resource, the behavior of the ETag response header is not + specified here, with the exception that a strong entity tag MUST NOT + be returned in the response. As a result, clients may need to + retrieve the modified calendar object resource (and ETag) as a basis + for further changes, rather than use the calendar object resource it + had sent with the PUT request. + +6. Calendaring Access Control + +6.1. Calendaring Privilege + + CalDAV servers MUST support and adhere to the requirements of WebDAV + ACL [RFC3744]. WebDAV ACL provides a framework for an extensible set + of privileges that can be applied to WebDAV collections and ordinary + resources. CalDAV servers MUST also support the calendaring + privilege defined in this section. + +6.1.1. CALDAV:read-free-busy Privilege + + Calendar users often wish to allow other users to see their busy time + information, without viewing the other details of the calendar + components (e.g., location, summary, attendees). This allows a + significant amount of privacy while still allowing other users to + schedule meetings at times when the user is likely to be free. + + The CALDAV:read-free-busy privilege controls which calendar + collections, regular collections, and calendar object resources are + examined when a CALDAV:free-busy-query REPORT request is processed + (see Section 7.10). This privilege can be granted on calendar + collections, regular collections, or calendar object resources. + + + + +Daboo, et al. Standards Track [Page 29] + +RFC 4791 CalDAV March 2007 + + + Servers MUST support this privilege on all calendar collections, + regular collections, and calendar object resources. + + + + + The CALDAV:read-free-busy privilege MUST be aggregated in the DAV: + read privilege. Servers MUST allow the CALDAV:read-free-busy to be + granted without the DAV:read privilege being granted. + + Clients should note that when only the CALDAV:read-free-busy + privilege has been granted on a resource, access to GET, HEAD, + OPTIONS, and PROPFIND on the resource is not implied (those + operations are governed by the DAV:read privilege). + +6.2. Additional Principal Property + + This section defines an additional property for WebDAV principal + resources, as defined in [RFC3744]. + +6.2.1. CALDAV:calendar-home-set Property + + Name: calendar-home-set + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Identifies the URL of any WebDAV collections that contain + calendar collections owned by the associated principal resource. + + Conformance: This property SHOULD be defined on a principal + resource. If defined, it MAY be protected and SHOULD NOT be + returned by a PROPFIND DAV:allprop request (as defined in Section + 12.14.1 of [RFC2518]). + + Description: The CALDAV:calendar-home-set property is meant to allow + users to easily find the calendar collections owned by the + principal. Typically, users will group all the calendar + collections that they own under a common collection. This + property specifies the URL of collections that are either calendar + collections or ordinary collections that have child or descendant + calendar collections owned by the principal. + + Definition: + + + + + + + + +Daboo, et al. Standards Track [Page 30] + +RFC 4791 CalDAV March 2007 + + + Example: + + + http://cal.example.com/home/bernard/calendars/ + + +7. Calendaring Reports + + This section defines the reports that CalDAV servers MUST support on + calendar collections and calendar object resources. + + CalDAV servers MUST advertise support for these reports on all + calendar collections and calendar object resources with the DAV: + supported-report-set property, defined in Section 3.1.5 of [RFC3253]. + CalDAV servers MAY also advertise support for these reports on + ordinary collections. + + Some of these reports allow calendar data (from possibly multiple + resources) to be returned. + +7.1. REPORT Method + + The REPORT method (defined in Section 3.6 of [RFC3253]) provides an + extensible mechanism for obtaining information about one or more + resources. Unlike the PROPFIND method, which returns the value of + one or more named properties, the REPORT method can involve more + complex processing. REPORT is valuable in cases where the server has + access to all of the information needed to perform the complex + request (such as a query), and where it would require multiple + requests for the client to retrieve the information needed to perform + the same request. + + CalDAV servers MUST support the DAV:expand-property REPORT defined in + Section 3.8 of [RFC3253]. + +7.2. Ordinary Collections + + Servers MAY support the reports defined in this document on ordinary + collections (collections that are not calendar collections), in + addition to calendar collections or calendar object resources. In + computing responses to the reports on ordinary collections, servers + MUST only consider calendar object resources contained in calendar + collections that are targeted by the REPORT request, based on the + value of the Depth request header. + + + + + + +Daboo, et al. Standards Track [Page 31] + +RFC 4791 CalDAV March 2007 + + +7.3. Date and Floating Time + + iCalendar provides a way to specify DATE and DATE-TIME values that + are not bound to any time zone in particular, hereafter called + "floating date" and "floating time", respectively. These values are + used to represent the same day, hour, minute, and second value, + regardless of which time zone is being observed. For instance, the + DATE value "20051111", represents November 11, 2005 in no specific + time zone, while the DATE-TIME value "20051111T111100" represents + November 11, 2005, at 11:11 A.M. in no specific time zone. + + CalDAV servers may need to convert "floating date" and "floating + time" values in date with UTC time values in the processing of + calendaring REPORT requests. + + For the CALDAV:calendar-query REPORT, CalDAV servers MUST rely on the + value of the CALDAV:timezone XML element, if specified as part of the + request body, to perform the proper conversion of "floating date" and + "floating time" values to date with UTC time values. If the CALDAV: + timezone XML element is not specified in the request body, CalDAV + servers MUST rely on the value of the CALDAV:calendar-timezone + property, if defined, or else the CalDAV servers MAY rely on the time + zone of their choice. + + For the CALDAV:free-busy-query REPORT, CalDAV servers MUST rely on + the value of the CALDAV:calendar-timezone property, if defined, to + compute the proper FREEBUSY time period value as date with UTC time + for calendar components scheduled with "floating date" or "floating + time". If the CALDAV:calendar-timezone property is not defined, + CalDAV servers MAY rely on the time zone of their choice. + +7.4. Time Range Filtering + + Some of the reports defined in this section can include a time range + filter that is used to restrict the set of calendar object resources + returned to just those that overlap the specified time range. The + time range filter can be applied to a calendar component as a whole, + or to specific calendar component properties with DATE or DATE-TIME + value types. + + To determine whether a calendar object resource matches the time + range filter element, the start and end times for the targeted + component or property are determined and then compared to the + requested time range. If there is an overlap with the requested time + range, then the calendar object resource matches the filter element. + The rules defined in [RFC2445] for determining the actual start and + end times of calendar components MUST be used, and these are fully + enumerated in Section 9.9 of this document. + + + +Daboo, et al. Standards Track [Page 32] + +RFC 4791 CalDAV March 2007 + + + When such time range filtering is used, special consideration must be + given to recurring calendar components, such as VEVENT and VTODO. + The server MUST expand recurring components to determine whether any + recurrence instances overlap the specified time range. If one or + more recurrence instances overlap the time range, then the calendar + object resource matches the filter element. + +7.5. Searching Text: Collations + + Some of the reports defined in this section do text matches of + character strings provided by the client and are compared to stored + calendar data. Since iCalendar data is, by default, encoded in the + UTF-8 charset and may include characters outside the US-ASCII charset + range in some property and parameter values, there is a need to + ensure that text matching follows well-defined rules. + + To deal with this, this specification makes use of the IANA Collation + Registry defined in [RFC4790] to specify collations that may be used + to carry out the text comparison operations with a well-defined rule. + + The comparisons used in CalDAV are all "substring" matches, as per + [RFC4790], Section 4.2. Collations supported by the server MUST + support "substring" match operations. + + CalDAV servers are REQUIRED to support the "i;ascii-casemap" and + "i;octet" collations, as described in [RFC4790], and MAY support + other collations. + + Servers MUST advertise the set of collations that they support via + the CALDAV:supported-collation-set property defined on any resource + that supports reports that use collations. + + Clients MUST only use collations from the list advertised by the + server. + + In the absence of a collation explicitly specified by the client, or + if the client specifies the "default" collation identifier (as + defined in [RFC4790], Section 3.1), the server MUST default to using + "i;ascii-casemap" as the collation. + + Wildcards (as defined in [RFC4790], Section 3.2) MUST NOT be used in + the collation identifier. + + If the client chooses a collation not supported by the server, the + server MUST respond with a CALDAV:supported-collation precondition + error response. + + + + + +Daboo, et al. Standards Track [Page 33] + +RFC 4791 CalDAV March 2007 + + +7.5.1. CALDAV:supported-collation-set Property + + Name: supported-collation-set + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Identifies the set of collations supported by the server + for text matching operations. + + Conformance: This property MUST be defined on any resource that + supports a report that does text matching. If defined, it MUST be + protected and SHOULD NOT be returned by a PROPFIND DAV:allprop + request (as defined in Section 12.14.1 of [RFC2518]). + + Description: The CALDAV:supported-collation-set property contains + zero or more CALDAV:supported-collation elements, which specify + the collection identifiers of the collations supported by the + server. + + Definition: + + + + + + Example: + + + i;ascii-casemap + i;octet + + +7.6. Partial Retrieval + + Some calendaring reports defined in this document allow partial + retrieval of calendar object resources. A CalDAV client can specify + what information to return in the body of a calendaring REPORT + request. + + A CalDAV client can request particular WebDAV property values, all + WebDAV property values, or a list of the names of the resource's + WebDAV properties. A CalDAV client can also request calendar data to + be returned and specify whether all calendar components and + properties should be returned, or only particular ones. See CALDAV: + calendar-data in Section 9.6. + + + + + +Daboo, et al. Standards Track [Page 34] + +RFC 4791 CalDAV March 2007 + + + By default, the returned calendar data will include the component + that defines the recurrence set, referred to as the "master + component", as well as the components that define exceptions to the + recurrence set, referred to as the "overridden components". + + A CalDAV client that is only interested in the recurrence instances + that overlap a specified time range can request to receive only the + "master component", along with the "overridden components" that + impact the specified time range, and thus, limit the data returned by + the server (see CALDAV:limit-recurrence-set in Section 9.6.6). An + overridden component impacts a time range if its current start and + end times overlap the time range, or if the original start and end + times -- the ones that would have been used if the instance were not + overridden -- overlap the time range, or if it affects other + instances that overlap the time range. + + A CalDAV client with no support for recurrence properties (i.e., + EXDATE, EXRULE, RDATE, and RRULE) and possibly VTIMEZONE components, + or a client unwilling to perform recurrence expansion because of + limited processing capability, can request to receive only the + recurrence instances that overlap a specified time range as separate + calendar components that each define exactly one recurrence instance + (see CALDAV:expand in Section 9.6.5.) + + Finally, in the case of VFREEBUSY components, a CalDAV client can + request to receive only the FREEBUSY property values that overlap a + specified time range (see CALDAV:limit-freebusy-set in + Section 9.6.7.) + +7.7. Non-Standard Components, Properties, and Parameters + + Servers MUST support the use of non-standard component, property, or + parameter names in the CALDAV:calendar-data XML element in + calendaring REPORT requests to allow clients to request that non- + standard components, properties, and parameters be returned in the + calendar data provided in the response. + + Servers MAY support the use of non-standard component, property, or + parameter names in the CALDAV:comp-filter, CALDAV:prop-filter, and + CALDAV:param-filter XML elements specified in the CALDAV:filter XML + element of calendaring REPORT requests. + + Servers MUST fail with the CALDAV:supported-filter precondition if a + calendaring REPORT request uses a CALDAV:comp-filter, CALDAV:prop- + filter, or CALDAV:param-filter XML element that makes reference to a + non-standard component, property, or parameter name on which the + server does not support queries. + + + + +Daboo, et al. Standards Track [Page 35] + +RFC 4791 CalDAV March 2007 + + +7.8. CALDAV:calendar-query REPORT + + The CALDAV:calendar-query REPORT performs a search for all calendar + object resources that match a specified filter. The response of this + report will contain all the WebDAV properties and calendar object + resource data specified in the request. In the case of the CALDAV: + calendar-data XML element, one can explicitly specify the calendar + components and properties that should be returned in the calendar + object resource data that matches the filter. + + The format of this report is modeled on the PROPFIND method. The + request and response bodies of the CALDAV:calendar-query REPORT use + XML elements that are also used by PROPFIND. In particular, the + request can include XML elements to request WebDAV properties to be + returned. When that occurs, the response should follow the same + behavior as PROPFIND with respect to the DAV:multistatus response + elements used to return specific property results. For instance, a + request to retrieve the value of a property that does not exist is an + error and MUST be noted with a response XML element that contains a + 404 (Not Found) status value. + + Support for the CALDAV:calendar-query REPORT is REQUIRED. + + Marshalling: + + The request body MUST be a CALDAV:calendar-query XML element, as + defined in Section 9.5. + + The request MAY include a Depth header. If no Depth header is + included, Depth:0 is assumed. + + The response body for a successful request MUST be a DAV: + multistatus XML element (i.e., the response uses the same format + as the response for PROPFIND). In the case where there are no + response elements, the returned DAV:multistatus XML element is + empty. + + The response body for a successful CALDAV:calendar-query REPORT + request MUST contain a DAV:response element for each iCalendar + object that matched the search filter. Calendar data is being + returned in the CALDAV:calendar-data XML element inside the DAV: + propstat XML element. + + Preconditions: + + (CALDAV:supported-calendar-data): The attributes "content-type" + and "version" of the CALDAV:calendar-data XML element (see + + + + +Daboo, et al. Standards Track [Page 36] + +RFC 4791 CalDAV March 2007 + + + Section 9.6) specify a media type supported by the server for + calendar object resources. + + (CALDAV:valid-filter): The CALDAV:filter XML element (see + Section 9.7) specified in the REPORT request MUST be valid. For + instance, a CALDAV:filter cannot nest a + element in a element, and a CALDAV:filter + cannot nest a element in a + element. + + (CALDAV:supported-filter): The CALDAV:comp-filter (see + Section 9.7.1), CALDAV:prop-filter (see Section 9.7.2), and + CALDAV:param-filter (see Section 9.7.3) XML elements used in the + CALDAV:filter XML element (see Section 9.7) in the REPORT request + only make reference to components, properties, and parameters for + which queries are supported by the server, i.e., if the CALDAV: + filter element attempts to reference an unsupported component, + property, or parameter, this precondition is violated. Servers + SHOULD report the CALDAV:comp-filter, CALDAV:prop-filter, or + CALDAV:param-filter for which it does not provide support. + + + + (CALDAV:valid-calendar-data): The time zone specified in the + REPORT request MUST be a valid iCalendar object containing a + single valid VTIMEZONE component. + + (CALDAV:min-date-time): Any XML element specifying a range of time + MUST have its start or end DATE or DATE-TIME values greater than + or equal to the value of the CALDAV:min-date-time property value + (Section 5.2.6) on the calendar collections being targeted by the + REPORT request; + + (CALDAV:max-date-time): Any XML element specifying a range of time + MUST have its start or end DATE or DATE-TIME values less than or + equal to the value of the CALDAV:max-date-time property value + (Section 5.2.7) on the calendar collections being targeted by the + REPORT request; + + (CALDAV:supported-collation): Any XML attribute specifying a + collation MUST specify a collation supported by the server as + described in Section 7.5. + + + + + + + +Daboo, et al. Standards Track [Page 37] + +RFC 4791 CalDAV March 2007 + + + Postconditions: + + (DAV:number-of-matches-within-limits): The number of matching + calendar object resources must fall within server-specific, + predefined limits. For example, this condition might be triggered + if a search specification would cause the return of an extremely + large number of responses. + +7.8.1. Example: Partial Retrieval of Events by Time Range + + In this example, the client requests the server to return specific + components and properties of the VEVENT components that overlap the + time range from January 4, 2006, at 00:00:00 A.M. UTC to January 5, + 2006, at 00:00:00 A.M. UTC. In addition, the DAV:getetag property is + also requested and returned as part of the response. Note that the + first calendar object returned is a recurring event whose first + instance lies outside the requested time range, but whose third + instance does overlap the time range. Note that due to the CALDAV: + calendar-data element restrictions, the DTSTAMP property in VEVENT + components has not been returned, and the only property returned in + the VCALENDAR object is VERSION. + + See Appendix B for the calendar data being targeted by this example. + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 38] + +RFC 4791 CalDAV March 2007 + + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + +Daboo, et al. Standards Track [Page 39] + +RFC 4791 CalDAV March 2007 + + + + + + http://cal.example.com/bernard/work/abcd2.ics + + + "fffff-abcd2" + BEGIN:VCALENDAR + VERSION:2.0 + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTART;TZID=US/Eastern:20060102T120000 + DURATION:PT1H + RRULE:FREQ=DAILY;COUNT=5 + SUMMARY:Event #2 + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + DTSTART;TZID=US/Eastern:20060104T140000 + DURATION:PT1H + RECURRENCE-ID;TZID=US/Eastern:20060104T120000 + SUMMARY:Event #2 bis + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + DTSTART;TZID=US/Eastern:20060106T140000 + DURATION:PT1H + RECURRENCE-ID;TZID=US/Eastern:20060106T120000 + SUMMARY:Event #2 bis bis + UID:00959BC664CA650E933C892C@example.com + + + +Daboo, et al. Standards Track [Page 40] + +RFC 4791 CalDAV March 2007 + + + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + http://cal.example.com/bernard/work/abcd3.ics + + + "fffff-abcd3" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTART;TZID=US/Eastern:20060104T100000 + DURATION:PT1H + SUMMARY:Event #3 + UID:DC6C50A017428C5216A2F1CD@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + + + + +Daboo, et al. Standards Track [Page 41] + +RFC 4791 CalDAV March 2007 + + +7.8.2. Example: Partial Retrieval of Recurring Events + + In this example, the client requests the server to return VEVENT + components that overlap the time range from January 3, 2006, at 00: + 00:00 A.M. UTC to January 5, 2006, at 00:00:00 A.M. UTC. Use of the + CALDAV:limit-recurrence-set element causes the server to only return + overridden recurrence components that overlap the time range + specified in that element or that affect other instances that overlap + the time range (e.g., in the case of a THISANDFUTURE behavior). In + this example, the first overridden component in the matching resource + is returned, but the second one is not. + + See Appendix B for the calendar data being targeted by this example. + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + +Daboo, et al. Standards Track [Page 42] + +RFC 4791 CalDAV March 2007 + + + + + + http://cal.example.com/bernard/work/abcd2.ics + + + "fffff-abcd2" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060102T120000 + DURATION:PT1H + RRULE:FREQ=DAILY;COUNT=5 + SUMMARY:Event #2 + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060104T140000 + DURATION:PT1H + RECURRENCE-ID;TZID=US/Eastern:20060104T120000 + SUMMARY:Event #2 bis + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + END:VCALENDAR + + + + + +Daboo, et al. Standards Track [Page 43] + +RFC 4791 CalDAV March 2007 + + + HTTP/1.1 200 OK + + + + http://cal.example.com/bernard/work/abcd3.ics + + + "fffff-abcd3" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com + ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com + DTSTAMP:20060206T001220Z + DTSTART;TZID=US/Eastern:20060104T100000 + DURATION:PT1H + LAST-MODIFIED:20060206T001330Z + ORGANIZER:mailto:cyrus@example.com + SEQUENCE:1 + STATUS:TENTATIVE + SUMMARY:Event #3 + UID:DC6C50A017428C5216A2F1CD@example.com + X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + +Daboo, et al. Standards Track [Page 44] + +RFC 4791 CalDAV March 2007 + + + + + +7.8.3. Example: Expanded Retrieval of Recurring Events + + In this example, the client requests the server to return VEVENT + components that overlap the time range from January 2, 2006, at 00: + 00:00 A.M. UTC to January 5, 2006, at 00:00:00 A.M. UTC and to return + recurring calendar components expanded into individual recurrence + instance calendar components. Use of the CALDAV:expand element + causes the server to only return overridden recurrence instances that + overlap the time range specified in that element. + + See Appendix B for the calendar data being targeted by this example. + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + +Daboo, et al. Standards Track [Page 45] + +RFC 4791 CalDAV March 2007 + + + + + + http://cal.example.com/bernard/work/abcd2.ics + + + "fffff-abcd2" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART:20060103T170000 + DURATION:PT1H + RECURRENCE-ID:20060103T170000 + SUMMARY:Event #2 + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART:20060104T190000 + DURATION:PT1H + RECURRENCE-ID:20060104T170000 + SUMMARY:Event #2 bis + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + http://cal.example.com/bernard/work/abcd3.ics + + + "fffff-abcd3" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VEVENT + ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com + ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com + DTSTAMP:20060206T001220Z + DTSTART:20060104T150000 + DURATION:PT1H + LAST-MODIFIED:20060206T001330Z + + + +Daboo, et al. Standards Track [Page 46] + +RFC 4791 CalDAV March 2007 + + + ORGANIZER:mailto:cyrus@example.com + SEQUENCE:1 + STATUS:TENTATIVE + SUMMARY:Event #3 + UID:DC6C50A017428C5216A2F1CD@example.com + X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 47] + +RFC 4791 CalDAV March 2007 + + +7.8.4. Example: Partial Retrieval of Stored Free Busy Components + + In this example, the client requests the server to return the + VFREEBUSY components that have free busy information that overlap the + time range from January 2, 2006, at 00:00:00 A.M. UTC (inclusively) + to January 3, 2006, at 00:00:00 A.M. UTC (exclusively). Use of the + CALDAV:limit-freebusy-set element causes the server to only return + the FREEBUSY property values that overlap the time range specified in + that element. Note that this is not an example of discovering when + the calendar owner is busy. + + See Appendix B for the calendar data being targeted by this example. + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 48] + +RFC 4791 CalDAV March 2007 + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://cal.example.com/bernard/work/abcd8.ics + + + "fffff-abcd8" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VFREEBUSY + ORGANIZER;CN="Bernard Desruisseaux":mailto:bernard@example.com + UID:76ef34-54a3d2@example.com + DTSTAMP:20050530T123421Z + DTSTART:20060101T100000Z + DTEND:20060108T100000Z + FREEBUSY;FBTYPE=BUSY-TENTATIVE:20060102T100000Z/20060102T120000Z + END:VFREEBUSY + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 49] + +RFC 4791 CalDAV March 2007 + + +7.8.5. Example: Retrieval of To-Dos by Alarm Time Range + + In this example, the client requests the server to return the VTODO + components that have an alarm trigger scheduled in the specified time + range. + + See Appendix B for the calendar data being targeted by this example. + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 50] + +RFC 4791 CalDAV March 2007 + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://cal.example.com/bernard/work/abcd4.ics + + + "fffff-abcd4" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTODO + DTSTAMP:20060205T235300Z + DUE;TZID=US/Eastern:20060106T120000 + LAST-MODIFIED:20060205T235308Z + SEQUENCE:1 + STATUS:NEEDS-ACTION + SUMMARY:Task #2 + UID:E10BA47467C5C69BB74E8720@example.com + BEGIN:VALARM + ACTION:AUDIO + TRIGGER;RELATED=START:-PT10M + END:VALARM + END:VTODO + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + +7.8.6. Example: Retrieval of Event by UID + + In this example, the client requests the server to return the VEVENT + component that has the UID property set to + "DC6C50A017428C5216A2F1CD@example.com". + + See Appendix B for the calendar data being targeted by this example. + + + + + +Daboo, et al. Standards Track [Page 51] + +RFC 4791 CalDAV March 2007 + + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + DC6C50A017428C5216A2F1CD@example.com + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://cal.example.com/bernard/work/abcd3.ics + + + "fffff-abcd3" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + + + +Daboo, et al. Standards Track [Page 52] + +RFC 4791 CalDAV March 2007 + + + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com + ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com + DTSTAMP:20060206T001220Z + DTSTART;TZID=US/Eastern:20060104T100000 + DURATION:PT1H + LAST-MODIFIED:20060206T001330Z + ORGANIZER:mailto:cyrus@example.com + SEQUENCE:1 + STATUS:TENTATIVE + SUMMARY:Event #3 + UID:DC6C50A017428C5216A2F1CD@example.com + X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + +7.8.7. Example: Retrieval of Events by PARTSTAT + + In this example, the client requests the server to return the VEVENT + components that have the ATTENDEE property with the value + "mailto:lisa@example.com" and for which the PARTSTAT parameter is set + to NEEDS-ACTION. + + See Appendix B for the calendar data being targeted by this example. + + + + + + + +Daboo, et al. Standards Track [Page 53] + +RFC 4791 CalDAV March 2007 + + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + mailto:lisa@example.com + + NEEDS-ACTION + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://cal.example.com/bernard/work/abcd3.ics + + + "fffff-abcd3" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + + + +Daboo, et al. Standards Track [Page 54] + +RFC 4791 CalDAV March 2007 + + + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com + ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com + DTSTAMP:20060206T001220Z + DTSTART;TZID=US/Eastern:20060104T100000 + DURATION:PT1H + LAST-MODIFIED:20060206T001330Z + ORGANIZER:mailto:cyrus@example.com + SEQUENCE:1 + STATUS:TENTATIVE + SUMMARY:Event #3 + UID:DC6C50A017428C5216A2F1CD@example.com + X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + +7.8.8. Example: Retrieval of Events Only + + In this example, the client requests the server to return all VEVENT + components. + + See Appendix B for the calendar data being targeted by this example. + + + + + +Daboo, et al. Standards Track [Page 55] + +RFC 4791 CalDAV March 2007 + + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://cal.example.com/bernard/work/abcd1.ics + + + "fffff-abcd1" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + + + +Daboo, et al. Standards Track [Page 56] + +RFC 4791 CalDAV March 2007 + + + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTAMP:20060206T001102Z + DTSTART;TZID=US/Eastern:20060102T100000 + DURATION:PT1H + SUMMARY:Event #1 + Description:Go Steelers! + UID:74855313FA803DA593CD579A@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + http://cal.example.com/bernard/work/abcd2.ics + + + "fffff-abcd2" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + + + +Daboo, et al. Standards Track [Page 57] + +RFC 4791 CalDAV March 2007 + + + END:VTIMEZONE + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060102T120000 + DURATION:PT1H + RRULE:FREQ=DAILY;COUNT=5 + SUMMARY:Event #2 + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060104T140000 + DURATION:PT1H + RECURRENCE-ID;TZID=US/Eastern:20060104T120000 + SUMMARY:Event #2 bis + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060106T140000 + DURATION:PT1H + RECURRENCE-ID;TZID=US/Eastern:20060106T120000 + SUMMARY:Event #2 bis bis + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + http://cal.example.com/bernard/work/abcd3.ics + + + "fffff-abcd3" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + + + +Daboo, et al. Standards Track [Page 58] + +RFC 4791 CalDAV March 2007 + + + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com + ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com + DTSTAMP:20060206T001220Z + DTSTART;TZID=US/Eastern:20060104T100000 + DURATION:PT1H + LAST-MODIFIED:20060206T001330Z + ORGANIZER:mailto:cyrus@example.com + SEQUENCE:1 + STATUS:TENTATIVE + SUMMARY:Event #3 + UID:DC6C50A017428C5216A2F1CD@example.com + X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + +7.8.9. Example: Retrieval of All Pending To-Dos + + In this example, the client requests the server to return all VTODO + components that do not include a COMPLETED property and do not have a + STATUS property value matching CANCELLED, i.e., VTODOs that still + need to be worked on. + + See Appendix B for the calendar data being targeted by this example. + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 59] + +RFC 4791 CalDAV March 2007 + + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + CANCELLED + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://cal.example.com/bernard/work/abcd4.ics + + + "fffff-abcd4" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTODO + + + +Daboo, et al. Standards Track [Page 60] + +RFC 4791 CalDAV March 2007 + + + DTSTAMP:20060205T235335Z + DUE;VALUE=DATE:20060104 + STATUS:NEEDS-ACTION + SUMMARY:Task #1 + UID:DDDEEB7915FA61233B861457@example.com + BEGIN:VALARM + ACTION:AUDIO + TRIGGER;RELATED=START:-PT10M + END:VALARM + END:VTODO + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + http://cal.example.com/bernard/work/abcd5.ics + + + "fffff-abcd5" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTODO + DTSTAMP:20060205T235300Z + DUE;VALUE=DATE:20060106 + LAST-MODIFIED:20060205T235308Z + SEQUENCE:1 + STATUS:NEEDS-ACTION + SUMMARY:Task #2 + UID:E10BA47467C5C69BB74E8720@example.com + BEGIN:VALARM + ACTION:AUDIO + TRIGGER;RELATED=START:-PT10M + END:VALARM + END:VTODO + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + + + + + +Daboo, et al. Standards Track [Page 61] + +RFC 4791 CalDAV March 2007 + + +7.8.10. Example: Attempt to Query Unsupported Property + + In this example, the client requests the server to return all VEVENT + components that include an X-ABC-GUID property with a value matching + "ABC". However, the server does not support querying that non- + standard property, and instead returns an error response. + + See Appendix B for the calendar data being targeted by this example. + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + ABC + + + + + + + >> Response << + + HTTP/1.1 403 Forbidden + Date: Sat, 11 Nov 2005 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + +Daboo, et al. Standards Track [Page 62] + +RFC 4791 CalDAV March 2007 + + +7.9. CALDAV:calendar-multiget REPORT + + The CALDAV:calendar-multiget REPORT is used to retrieve specific + calendar object resources from within a collection, if the Request- + URI is a collection, or to retrieve a specific calendar object + resource, if the Request-URI is a calendar object resource. This + report is similar to the CALDAV:calendar-query REPORT (see + Section 7.8), except that it takes a list of DAV:href elements, + instead of a CALDAV:filter element, to determine which calendar + object resources to return. + + Support for the CALDAV:calendar-multiget REPORT is REQUIRED. + + Marshalling: + + The request body MUST be a CALDAV:calendar-multiget XML element + (see Section 9.10). If the Request-URI is a collection resource, + then the DAV:href elements MUST refer to calendar object resources + within that collection, and they MAY refer to calendar object + resources at any depth within the collection. As a result, the + "Depth" header MUST be ignored by the server and SHOULD NOT be + sent by the client. If the Request-URI refers to a non-collection + resource, then there MUST be a single DAV:href element that is + equivalent to the Request-URI. + + The response body for a successful request MUST be a DAV: + multistatus XML element. + + The response body for a successful CALDAV:calendar-multiget REPORT + request MUST contain a DAV:response element for each calendar + object resource referenced by the provided set of DAV:href + elements. Calendar data is being returned in the CALDAV:calendar- + data element inside the DAV:prop element. + + In the case of an error accessing any of the provided DAV:href + resources, the server MUST return the appropriate error status + code in the DAV:status element of the corresponding DAV:response + element. + + Preconditions: + + (CALDAV:supported-calendar-data): The attributes "content-type" + and "version" of the CALDAV:calendar-data XML elements (see + Section 9.6) specify a media type supported by the server for + calendar object resources. + + (CALDAV:min-date-time): Any XML element specifying a range of time + MUST have its start or end DATE or DATE-TIME values greater than + + + +Daboo, et al. Standards Track [Page 63] + +RFC 4791 CalDAV March 2007 + + + or equal to the value of the CALDAV:min-date-time property value + (Section 5.2.6) on the calendar collections being targeted by the + REPORT request; + + (CALDAV:max-date-time): Any XML element specifying a range of time + MUST have its start or end DATE or DATE-TIME values less than or + equal to the value of the CALDAV:max-date-time property value + (Section 5.2.7) on the calendar collections being targeted by the + REPORT request; + + Postconditions: + + None. + +7.9.1. Example: Successful CALDAV:calendar-multiget REPORT + + In this example, the client requests the server to return specific + properties of the VEVENT components referenced by specific URIs. In + addition, the DAV:getetag property is also requested and returned as + part of the response. Note that in this example, the resource at + http://cal.example.com/bernard/work/mtg1.ics does not exist, + resulting in an error status response. + + See Appendix B for the calendar data being targeted by this example. + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + /bernard/work/abcd1.ics + /bernard/work/mtg1.ics + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + + + +Daboo, et al. Standards Track [Page 64] + +RFC 4791 CalDAV March 2007 + + + Content-Length: xxxx + + + + + http://cal.example.com/bernard/work/abcd1.ics + + + "fffff-abcd1" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTAMP:20060206T001102Z + DTSTART;TZID=US/Eastern:20060102T100000 + DURATION:PT1H + SUMMARY:Event #1 + Description:Go Steelers! + UID:74855313FA803DA593CD579A@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + http://cal.example.com/bernard/work/mtg1.ics + HTTP/1.1 404 Not Found + + + +Daboo, et al. Standards Track [Page 65] + +RFC 4791 CalDAV March 2007 + + + + + +7.10. CALDAV:free-busy-query REPORT + + The CALDAV:free-busy-query REPORT generates a VFREEBUSY component + containing free busy information for all the calendar object + resources targeted by the request and that have the CALDAV:read-free- + busy or DAV:read privilege granted to the current user. + + Only VEVENT components without a TRANSP property or with the TRANSP + property set to OPAQUE, and VFREEBUSY components SHOULD be considered + in generating the free busy time information. + + In the case of VEVENT components, the free or busy time type (FBTYPE) + of the FREEBUSY properties in the returned VFREEBUSY component SHOULD + be derived from the value of the TRANSP and STATUS properties, as + outlined in the table below: + + +---------------------------++------------------+ + | VEVENT || VFREEBUSY | + +-------------+-------------++------------------+ + | TRANSP | STATUS || FBTYPE | + +=============+=============++==================+ + | | CONFIRMED || BUSY | + | | (default) || | + | OPAQUE +-------------++------------------+ + | (default) | CANCELLED || FREE | + | +-------------++------------------+ + | | TENTATIVE || BUSY-TENTATIVE | + | +-------------++------------------+ + | | x-name || BUSY or | + | | || x-name | + +-------------+-------------++------------------+ + | | CONFIRMED || | + | TRANSPARENT | CANCELLED || FREE | + | | TENTATIVE || | + | | x-name || | + +-------------+-------------++------------------+ + + Duplicate busy time periods with the same FBTYPE parameter value + SHOULD NOT be specified in the returned VFREEBUSY component. Servers + SHOULD coalesce consecutive or overlapping busy time periods of the + same type. Busy time periods with different FBTYPE parameter values + MAY overlap. + + Support for the CALDAV:free-busy-query REPORT is REQUIRED. + + + + +Daboo, et al. Standards Track [Page 66] + +RFC 4791 CalDAV March 2007 + + + Marshalling: + + The request body MUST be a CALDAV:free-busy-query XML element (see + Section 9.11), which MUST contain exactly one CALDAV:time-range + XML element, as defined in Section 9.9. + + The request MAY include a Depth header. If no Depth header is + included, Depth:0 is assumed. + + The response body for a successful request MUST be an iCalendar + object that contains exactly one VFREEBUSY component that + describes the busy time intervals for the calendar object + resources containing VEVENT, or VFREEBUSY components that satisfy + the Depth value and for which the current user is at least granted + the CALDAV:read-free-busy privilege. If no calendar object + resources are found to satisfy these conditions, a VFREEBUSY + component with no FREEBUSY property MUST be returned. This report + only returns busy time information. Free time information can be + inferred from the returned busy time information. + + If the current user is not granted the CALDAV:read-free-busy or + DAV:read privileges on the Request-URI, the CALDAV:free-busy-query + REPORT request MUST fail and return a 404 (Not Found) status + value. This restriction will prevent users from discovering URLs + of resources for which they are only granted the CALDAV:read-free- + busy privilege. + + The CALDAV:free-busy-query REPORT request can only be run against + a collection (either a regular collection or a calendar + collection). An attempt to run the report on a calendar object + resource MUST fail and return a 403 (Forbidden) status value. + + Preconditions: + + None. + + Postconditions: + + (DAV:number-of-matches-within-limits): The number of matching + calendar object resources must fall within server-specific, + predefined limits. For example, this postcondition might fail if + the specified CALDAV:time-range would cause an extremely large + number of calendar object resources to be considered in computing + the response. + + + + + + + +Daboo, et al. Standards Track [Page 67] + +RFC 4791 CalDAV March 2007 + + +7.10.1. Example: Successful CALDAV:free-busy-query REPORT + + In this example, the client requests the server to return free busy + information on the calendar collection /bernard/work/, between 9:00 + A.M. and 5:00 P.M. EST (2:00 P.M. and 10:00 P.M. UTC) on the January + 4, 2006. The server responds, indicating two busy time intervals of + one hour, one of which is tentative. + + See Appendix B for the calendar data being targeted by this example. + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + >> Response << + + HTTP/1.1 200 OK + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: text/calendar + Content-Length: xxxx + + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Server//EN + BEGIN:VFREEBUSY + DTSTAMP:20050125T090000Z + DTSTART:20060104T140000Z + DTEND:20060105T220000Z + FREEBUSY;FBTYPE=BUSY-TENTATIVE:20060104T150000Z/PT1H + FREEBUSY:20060104T190000Z/PT1H + END:VFREEBUSY + END:VCALENDAR + + + + + + + + + +Daboo, et al. Standards Track [Page 68] + +RFC 4791 CalDAV March 2007 + + +8. Guidelines + +8.1. Client-to-Client Interoperability + + There are a number of actions clients can take that will be legal + (the server will not return errors), but that can degrade + interoperability with other client implementations accessing the same + data. For example, a recurrence rule could be replaced with a set of + recurrence dates, a single recurring event could be replaced with a + set of independent resources to represent each recurrence, or the + start/end time values can be translated from the original time zone + to another time zone. Although this advice amounts to iCalendar + interoperability best practices and is not limited only to CalDAV + usage, interoperability problems are likely to be more evident in + CalDAV use cases. + +8.2. Synchronization Operations + + WebDAV already provides functionality required to synchronize a + collection or set of collections, to make changes offline, and + provides a simple way to resolve conflicts when reconnected. ETags + are the key to making this work, but these are not required of all + WebDAV servers. Since offline functionality is more important to + calendar applications than to some other WebDAV applications, CalDAV + servers MUST support ETags, as specified in Section 5.3.4. + +8.2.1. Use of Reports + +8.2.1.1. Restrict the Time Range + + The reports provided in CalDAV can be used by clients to optimize + their performance in terms of network bandwidth usage and resource + consumption on the local client machine. Both are certainly major + considerations for mobile or handheld devices with limited capacity, + but they are also relevant to desktop client applications in cases + where the calendar collections contain large amounts of data. + + Typically, clients present calendar data to users in views that span + a finite time interval, so whenever possible, clients should only + retrieve calendar components from the server using CALDAV:calendar- + query REPORT, combined with a CALDAV:time-range element, to limit the + set of returned components to just those needed to populate the + current view. + + + + + + + + +Daboo, et al. Standards Track [Page 69] + +RFC 4791 CalDAV March 2007 + + +8.2.1.2. Synchronize by Time Range + + Typically in a calendar, historical data (events, to-dos, etc. that + have completed prior to the current date) do not change, though they + may be deleted. As a result, a client can speed up the + synchronization process by only considering data for the present time + and the future up to a reasonable limit (e.g., one week, one month). + If the user then tries to examine a portion of the calendar outside + the range that has been synchronized, the client can perform another + synchronization operation on the new time interval being examined. + This "just-in-time" synchronization can minimize bandwidth for common + user interaction behaviors. + +8.2.1.3. Synchronization Process + + If a client wants to support calendar data synchronization, as + opposed to downloading calendar data each time it is needed, the + client needs to cache the calendar object resource's URI and ETag, + along with the actual calendar data. While the URI remains static + for the lifetime of the calendar object resource, the ETag will + change with each successive change to the calendar object resource. + Thus, to synchronize a local data cache with the server, the client + can first fetch the URI/ETag pairs for the time interval being + considered, and compare those results with the cached data. Any + cached component whose ETag differs from that on the server needs to + be refreshed. + + In order to properly detect the changes between the server and client + data, the client will need to keep a record of which calendar object + resources have been created, changed, or deleted since the last + synchronization operation so that it can reconcile those changes with + the data on the server. + + Here's an example of how to do that: + + The client issues a CALDAV:calendar-query REPORT request for a + specific time range and asks for only the DAV:getetag property to be + returned: + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 70] + +RFC 4791 CalDAV March 2007 + + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + + The client then uses the results to determine which calendar object + resources have changed, been created, or deleted on the server, and + how those relate to locally cached calendar object resources that may + have changed, been created, or deleted. If the client determines + that there are calendar object resources on the server that need to + be fetched, the client issues a CALDAV:calendar-multiget REPORT + request to fetch its calendar data: + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + /bernard/work/abcd1.ics + /bernard/work/mtg1.ics + + + + + + + +Daboo, et al. Standards Track [Page 71] + +RFC 4791 CalDAV March 2007 + + +8.2.2. Restrict the Properties Returned + + A client may not need all the calendar properties of a calendar + object resource when presenting information to the user. Since some + calendar property values can be large (e.g., ATTACH or ATTENDEE), a + client can choose to restrict the calendar properties to be returned + in a calendaring REPORT request to those it knows it will use. + + However, if a client needs to make a change to a calendar object + resource, it can only change the entire calendar object resource via + a PUT request. There is currently no way to incrementally make a + change to a set of calendar properties of a calendar object resource. + As a result, the client will have to get the entire calendar object + resource that is being changed. + +8.3. Use of Locking + + WebDAV locks can be used to prevent two clients that are modifying + the same resource from either overwriting each others' changes + (though that problem can also be solved by using ETags) or wasting + time making changes that will conflict with another set of changes. + In a multi-user calendar system, an interactive calendar client could + lock an event while the user is editing the event, and unlock the + event when the user finishes or cancels. Locks can also be used to + prevent changes while data is being reorganized. For example, a + calendar client might lock two calendar collections prior to moving a + bunch of calendar resources from one to another. + + Clients are responsible for requesting a lock timeout period that is + appropriate to the use case. When the user explicitly decides to + reserve a resource and prevent other changes, a long timeout might be + appropriate, but in cases where the client automatically decides to + lock the resource, the timeout should be short (and the client can + always refresh the lock should it need to). A short lock timeout + means that if the client is unable to remove the lock, the other + calendar users aren't prevented from making changes. + +8.4. Finding Calendars + + Much of the time, a calendar client (or agent) will discover a new + calendar's location by being provided directly with the URL. For + example, a user will type his or her own calendar location into + client configuration information or copy and paste a URL from email + into the calendar application. The client need only confirm that the + URL points to a resource that is a calendar collection. The client + may also be able to browse WebDAV collections to find calendar + collections. + + + + +Daboo, et al. Standards Track [Page 72] + +RFC 4791 CalDAV March 2007 + + + The choice of HTTP URLs means that calendar object resources are + backward compatible with existing software, but does have the + disadvantage that existing software does not usually know to look at + the OPTIONS response to that URL to determine what can be done with + it. This is somewhat of a barrier for WebDAV usage as well as with + CalDAV usage. This specification does not offer a way through this + other than making the information available in the OPTIONS response + should this be requested. + + For calendar sharing and scheduling use cases, one might wish to find + the calendar belonging to another user. If the other user has a + calendar in the same repository, that calendar can be found by using + the principal namespace required by WebDAV ACL support. For other + cases, the authors have no universal solution, but implementers can + consider whether to use vCard [RFC2426] or LDAP [RFC4511] standards + together with calendar attributes [RFC2739]. + + Because CalDAV requires servers to support WebDAV ACL [RFC3744], + including principal namespaces, and with the addition of the CALDAV: + calendar-home-set property, there are a couple options for CalDAV + clients to find one's own calendar or another user's calendar. + + In this case, a DAV:principal-match REPORT is used to find a named + property (the CALDAV:calendar-home-set) on the Principal-URL of the + current user. Using this, a WebDAV client can learn "who am I" and + "where are my calendars". The REPORT request body looks like this: + + + + + + + + + + To find other users' calendars, the DAV:principal-property-search + REPORT can be used to filter on some properties and return others. + To search for a calendar owned by a user named "Laurie", the REPORT + request body would look like this: + + + + + + + + + + + +Daboo, et al. Standards Track [Page 73] + +RFC 4791 CalDAV March 2007 + + + + + + + + + Laurie + + + + + + + + The server performs a case-sensitive or caseless search for a + matching string subset of "Laurie" within the DAV:displayname + property. Thus, the server might return "Laurie Dusseault", "Laurier + Desruisseaux", or "Wilfrid Laurier" as matching DAV:displayname + values, and return the calendars for each of these. + +8.5. Storing and Using Attachments + + CalDAV clients MAY create attachments in calendar components either + as inline or external. This section contains some guidelines for + creating and managing attachments. + +8.5.1. Inline Attachments + + CalDAV clients MUST support inline attachments as specified in + iCalendar [RFC2445]. CalDAV servers MUST support inline attachments, + so clients can rely on being able to create attachments this way. On + the other hand, inline attachments have some drawbacks: + + o Servers MAY impose limitations on the size of calendar object + resources (i.e., refusing PUT requests of very large iCalendar + objects). Servers that impose such limitations MUST use the + CALDAV:max-resource-size property on a calendar collection to + inform the client as to what the limitation is (see + Section 5.2.5). + + o Servers MAY impose storage quota limitations on calendar + collections (See [RFC4331]). + + o Any change to a calendar object resource containing an inline + attachment requires the entire inline attachment to be re- + uploaded. + + + + +Daboo, et al. Standards Track [Page 74] + +RFC 4791 CalDAV March 2007 + + + o Clients synchronizing a changed calendar object resource have to + download the entire calendar object resource, even if the + attachment is unchanged. + +8.5.2. External Attachments + + CalDAV clients SHOULD support downloading of external attachments + referenced by arbitrary URI schemes, by either processing them + directly, or by passing the attachment URI to a suitable "helper + application" for processing, if such an application exists. CalDAV + clients MUST support downloading of external attachments referenced + by the "http" or "https" URI schemes. An external attachment could + be: + + o In a collection in the calendar collection containing the calendar + object resource; + + o Somewhere else in the same repository that hosts the calendar + collection; or + + o On an HTTP or FTP server elsewhere. + + CalDAV servers MAY provide support for child collections in calendar + collections. CalDAV servers MAY allow the MKCOL method to create + child collections in calendar collections. Child collections of + calendar collections MAY contain any type of resource except calendar + collections that they MUST NOT contain. Some CalDAV servers won't + allow child collections in calendar collections, and it may be + possible on such a server to discover other locations where + attachments can be stored. + + Clients are entirely responsible for maintaining reference + consistency with calendar components that link to external + attachments. A client deleting a calendar component with an external + attachment might therefore also delete the attachment if that's + appropriate; however, appropriateness can be very hard to determine. + A new component might easily reference some pre-existing Web resource + that is intended to have independent existence from the calendar + component (the "attachment" could be a major proposal to be discussed + in a meeting, for instance). Best practices will probably emerge and + should probably be documented, but for now, clients should be wary of + engaging in aggressive "cleanup" of external attachments. A client + could involve the user in making decisions about removing + unreferenced documents, or a client could be conservative in only + deleting attachments it had created. + + Also, clients are responsible for consistency of permissions when + using external attachments. One reason for servers to support the + + + +Daboo, et al. Standards Track [Page 75] + +RFC 4791 CalDAV March 2007 + + + storage of attachments within child collections of calendar + collections is that ACL inheritance might make it easier to grant the + same permissions to attachments that are granted on the calendar + collection. Otherwise, it can be very difficult to keep permissions + synchronized. With attachments stored on separate repositories, it + can be impossible to keep permissions consistent -- the two + repositories may not support the same permissions or have the same + set of principals. Some systems have used tickets or other anonymous + access control mechanisms to provide partially satisfactory solutions + to these kinds of problems. + +8.6. Storing and Using Alarms + + Note that all CalDAV calendar collections (including those the user + might treat as public or group calendars) can contain alarm + information on events and to-dos. Users can synchronize a calendar + between multiple devices and decide to have alarms execute on a + different device than the device that created the alarm. Not all + alarm action types are completely interoperable (e.g., those that + name a sound file to play). + + When the action is AUDIO and the client is configured to execute + the alarm, the client SHOULD play the suggested sound if it's + available or play another sound, but SHOULD NOT rewrite the alarm + just to replace the suggested sound with a sound that's locally + available. + + When the action is DISPLAY and the client is configured to execute + the alarm, the client SHOULD execute a display alarm by displaying + according to the suggested description or some reasonable + replacement, but SHOULD NOT rewrite the alarm for its own + convenience. + + When the action is EMAIL and the client is incapable of sending + email, it SHOULD ignore the alarm, but it MUST continue to + synchronize the alarm itself. + + This specification makes no recommendations about executing alarms + of type PROCEDURE, except to note that clients are advised to take + care to avoid creating security holes by executing these. + + Non-interoperable alarm information (e.g., should somebody define a + color to be used in a display alarm) should be put in non-standard + properties inside the VALARM component in order to keep the basic + alarm usable on all devices. + + Clients that allow changes to calendar object resources MUST + synchronize the alarm data that already exists in the resources. + + + +Daboo, et al. Standards Track [Page 76] + +RFC 4791 CalDAV March 2007 + + + Clients MAY execute alarms that are downloaded in this fashion, + possibly based on user preference. If a client is only doing read + operations on a calendar and there is no risk of losing alarm + information, then the client MAY discard alarm information. + + This specification makes no attempt to provide multi-user alarms on + group calendars or to find out for whom an alarm is intended. + Addressing those issues might require extensions to iCalendar; for + example, to store alarms per-user, or to indicate for which user a + VALARM was intended. In the meantime, clients might maximize + interoperability by generally not uploading alarm information to + public, group, or resource calendars. + +9. XML Element Definitions + +9.1. CALDAV:calendar XML Element + + Name: calendar + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies the resource type of a calendar collection. + + Description: See Section 4.2. + + Definition: + + + +9.2. CALDAV:mkcalendar XML Element + + Name: mkcalendar + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a request that includes the WebDAV property + values to be set for a calendar collection resource when it is + created. + + Description: See Section 5.3.1. + + Definition: + + + + + + + + + +Daboo, et al. Standards Track [Page 77] + +RFC 4791 CalDAV March 2007 + + +9.3. CALDAV:mkcalendar-response XML Element + + Name: mkcalendar-response + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a response body for a successful MKCALENDAR + request. + + Description: See Section 5.3.1. + + Definition: + + + +9.4. CALDAV:supported-collation XML Element + + Name: supported-collation + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Identifies a single collation via its collation identifier, + as defined by [RFC4790]. + + Description: The CALDAV:supported-collation contains the text of a + collation identifier, as described in Section 7.5.1. + + Definition: + + + PCDATA value: collation identifier + +9.5. CALDAV:calendar-query XML Element + + Name: calendar-query + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Defines a report for querying calendar object resources. + + Description: See Section 7.8. + + Definition: + + + + + + +Daboo, et al. Standards Track [Page 78] + +RFC 4791 CalDAV March 2007 + + +9.6. CALDAV:calendar-data XML Element + + Name: calendar-data + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specified one of the following: + + 1. A supported media type for calendar object resources when + nested in the CALDAV:supported-calendar-data property; + + 2. The parts of a calendar object resource should be returned by + a calendaring report; + + 3. The content of a calendar object resource in a response to a + calendaring report. + + Description: When nested in the CALDAV:supported-calendar-data + property, the CALDAV:calendar-data XML element specifies a media + type supported by the CalDAV server for calendar object resources. + + When used in a calendaring REPORT request, the CALDAV:calendar- + data XML element specifies which parts of calendar object + resources need to be returned in the response. If the CALDAV: + calendar-data XML element doesn't contain any CALDAV:comp element, + calendar object resources will be returned in their entirety. + + Finally, when used in a calendaring REPORT response, the CALDAV: + calendar-data XML element specifies the content of a calendar + object resource. Given that XML parsers normalize the two- + character sequence CRLF (US-ASCII decimal 13 and US-ASCII decimal + 10) to a single LF character (US-ASCII decimal 10), the CR + character (US-ASCII decimal 13) MAY be omitted in calendar object + resources specified in the CALDAV:calendar-data XML element. + Furthermore, calendar object resources specified in the CALDAV: + calendar-data XML element MAY be invalid per their media type + specification if the CALDAV:calendar-data XML element part of the + calendaring REPORT request did not specify required properties + (e.g., UID, DTSTAMP, etc.), or specified a CALDAV:prop XML element + with the "novalue" attribute set to "yes". + + Note: The CALDAV:calendar-data XML element is specified in requests + and responses inside the DAV:prop XML element as if it were a + WebDAV property. However, the CALDAV:calendar-data XML element is + not a WebDAV property and, as such, is not returned in PROPFIND + responses, nor used in PROPPATCH requests. + + + + + +Daboo, et al. Standards Track [Page 79] + +RFC 4791 CalDAV March 2007 + + + Note: The iCalendar data embedded within the CALDAV:calendar-data + XML element MUST follow the standard XML character data encoding + rules, including use of <, >, & etc. entity encoding or + the use of a construct. In the later case, the + iCalendar data cannot contain the character sequence "]]>", which + is the end delimiter for the CDATA section. + + Definition: + + + + when nested in the CALDAV:supported-calendar-data property + to specify a supported media type for calendar object + resources; + + + + when nested in the DAV:prop XML element in a calendaring + REPORT request to specify which parts of calendar object + resources should be returned in the response; + + + PCDATA value: iCalendar object + + when nested in the DAV:prop XML element in a calendaring + REPORT response to specify the content of a returned + calendar object resource. + + + content-type value: a MIME media type + version value: a version string + + attributes can be used on all three variants of the + CALDAV:calendar-data XML element. + +9.6.1. CALDAV:comp XML Element + + Name: comp + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Defines which component types to return. + + + + + + +Daboo, et al. Standards Track [Page 80] + +RFC 4791 CalDAV March 2007 + + + Description: The name value is a calendar component name (e.g., + VEVENT). + + Definition: + + + + + name value: a calendar component name + + Note: The CALDAV:prop and CALDAV:allprop elements have the same name + as the DAV:prop and DAV:allprop elements defined in [RFC2518]. + However, the CALDAV:prop and CALDAV:allprop elements are defined + in the "urn:ietf:params:xml:ns:caldav" namespace instead of the + "DAV:" namespace. + +9.6.2. CALDAV:allcomp XML Element + + Name: allcomp + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies that all components shall be returned. + + Description: The CALDAV:allcomp XML element can be used when the + client wants all types of components returned by a calendaring + REPORT request. + + Definition: + + + +9.6.3. CALDAV:allprop XML Element + + Name: allprop + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies that all properties shall be returned. + + Description: The CALDAV:allprop XML element can be used when the + client wants all properties of components returned by a + calendaring REPORT request. + + Definition: + + + + + + +Daboo, et al. Standards Track [Page 81] + +RFC 4791 CalDAV March 2007 + + + Note: The CALDAV:allprop element has the same name as the DAV: + allprop element defined in [RFC2518]. However, the CALDAV:allprop + element is defined in the "urn:ietf:params:xml:ns:caldav" + namespace instead of the "DAV:" namespace. + +9.6.4. CALDAV:prop XML Element + + Name: prop + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Defines which properties to return in the response. + + Description: The "name" attribute specifies the name of the calendar + property to return (e.g., ATTENDEE). The "novalue" attribute can + be used by clients to request that the actual value of the + property not be returned (if the "novalue" attribute is set to + "yes"). In that case, the server will return just the iCalendar + property name and any iCalendar parameters and a trailing ":" + without the subsequent value data. + + Definition: + + + + + name value: a calendar property name + novalue value: "yes" or "no" + + Note: The CALDAV:prop element has the same name as the DAV:prop + element defined in [RFC2518]. However, the CALDAV:prop element is + defined in the "urn:ietf:params:xml:ns:caldav" namespace instead + of the "DAV:" namespace. + +9.6.5. CALDAV:expand XML Element + + Name: expand + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Forces the server to expand recurring components into + individual recurrence instances. + + Description: The CALDAV:expand XML element specifies that for a + given calendaring REPORT request, the server MUST expand the + recurrence set into calendar components that define exactly one + + + + +Daboo, et al. Standards Track [Page 82] + +RFC 4791 CalDAV March 2007 + + + recurrence instance, and MUST return only those whose scheduled + time intersect a specified time range. + + The "start" attribute specifies the inclusive start of the time + range, and the "end" attribute specifies the non-inclusive end of + the time range. Both attributes are specified as date with UTC + time value. The value of the "end" attribute MUST be greater than + the value of the "start" attribute. + + The server MUST use the same logic as defined for CALDAV:time- + range to determine if a recurrence instance intersects the + specified time range. + + Recurring components, other than the initial instance, MUST + include a RECURRENCE-ID property indicating which instance they + refer to. + + The returned calendar components MUST NOT use recurrence + properties (i.e., EXDATE, EXRULE, RDATE, and RRULE) and MUST NOT + have reference to or include VTIMEZONE components. Date and local + time with reference to time zone information MUST be converted + into date with UTC time. + + Definition: + + + + + start value: an iCalendar "date with UTC time" + end value: an iCalendar "date with UTC time" + +9.6.6. CALDAV:limit-recurrence-set XML Element + + Name: limit-recurrence-set + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a time range to limit the set of "overridden + components" returned by the server. + + Description: The CALDAV:limit-recurrence-set XML element specifies + that for a given calendaring REPORT request, the server MUST + return, in addition to the "master component", only the + "overridden components" that impact a specified time range. An + overridden component impacts a time range if its current start and + end times overlap the time range, or if the original start and end + + + + +Daboo, et al. Standards Track [Page 83] + +RFC 4791 CalDAV March 2007 + + + times -- the ones that would have been used if the instance were + not overridden -- overlap the time range. + + The "start" attribute specifies the inclusive start of the time + range, and the "end" attribute specifies the non-inclusive end of + the time range. Both attributes are specified as date with UTC + time value. The value of the "end" attribute MUST be greater than + the value of the "start" attribute. + + The server MUST use the same logic as defined for CALDAV:time- + range to determine if the current or original scheduled time of an + "overridden" recurrence instance intersects the specified time + range. + + Overridden components that have a RANGE parameter on their + RECURRENCE-ID property may specify one or more instances in the + recurrence set, and some of those instances may fall within the + specified time range or may have originally fallen within the + specified time range prior to being overridden. If that is the + case, the overridden component MUST be included in the results, as + it has a direct impact on the interpretation of instances within + the specified time range. + + Definition: + + + + + start value: an iCalendar "date with UTC time" + end value: an iCalendar "date with UTC time" + +9.6.7. CALDAV:limit-freebusy-set XML Element + + Name: limit-freebusy-set + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a time range to limit the set of FREEBUSY values + returned by the server. + + Description: The CALDAV:limit-freebusy-set XML element specifies + that for a given calendaring REPORT request, the server MUST only + return the FREEBUSY property values of a VFREEBUSY component that + intersects a specified time range. + + The "start" attribute specifies the inclusive start of the time + range, and the "end" attribute specifies the non-inclusive end of + + + +Daboo, et al. Standards Track [Page 84] + +RFC 4791 CalDAV March 2007 + + + the time range. Both attributes are specified as "date with UTC + time" value. The value of the "end" attribute MUST be greater + than the value of the "start" attribute. + + The server MUST use the same logic as defined for CALDAV:time- + range to determine if a FREEBUSY property value intersects the + specified time range. + + Definition: + + + + + start value: an iCalendar "date with UTC time" + end value: an iCalendar "date with UTC time" + +9.7. CALDAV:filter XML Element + + Name: filter + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a filter to limit the set of calendar components + returned by the server. + + Description: The CALDAV:filter XML element specifies the search + filter used to limit the calendar components returned by a + calendaring REPORT request. + + Definition: + + + +9.7.1. CALDAV:comp-filter XML Element + + Name: comp-filter + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies search criteria on calendar components. + + Description: The CALDAV:comp-filter XML element specifies a query + targeted at the calendar object (i.e., VCALENDAR) or at a specific + calendar component type (e.g., VEVENT). The scope of the + CALDAV:comp-filter XML element is the calendar object when used as + a child of the CALDAV:filter XML element. The scope of the + CALDAV:comp-filter XML element is the enclosing calendar component + + + +Daboo, et al. Standards Track [Page 85] + +RFC 4791 CalDAV March 2007 + + + when used as a child of another CALDAV:comp-filter XML element. A + CALDAV:comp-filter is said to match if: + + * The CALDAV:comp-filter XML element is empty and the calendar + object or calendar component type specified by the "name" + attribute exists in the current scope; + + or: + + * The CALDAV:comp-filter XML element contains a CALDAV:is-not- + defined XML element and the calendar object or calendar + component type specified by the "name" attribute does not exist + in the current scope; + + or: + + * The CALDAV:comp-filter XML element contains a CALDAV:time-range + XML element and at least one recurrence instance in the + targeted calendar component is scheduled to overlap the + specified time range, and all specified CALDAV:prop-filter and + CALDAV:comp-filter child XML elements also match the targeted + calendar component; + + or: + + * The CALDAV:comp-filter XML element only contains CALDAV:prop- + filter and CALDAV:comp-filter child XML elements that all match + the targeted calendar component. + + Definition: + + + + + name value: a calendar object or calendar component + type (e.g., VEVENT) + +9.7.2. CALDAV:prop-filter XML Element + + Name: prop-filter + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies search criteria on calendar properties. + + Description: The CALDAV:prop-filter XML element specifies a query + targeted at a specific calendar property (e.g., CATEGORIES) in the + + + +Daboo, et al. Standards Track [Page 86] + +RFC 4791 CalDAV March 2007 + + + scope of the enclosing calendar component. A calendar property is + said to match a CALDAV:prop-filter if: + + * The CALDAV:prop-filter XML element is empty and a property of + the type specified by the "name" attribute exists in the + enclosing calendar component; + + or: + + * The CALDAV:prop-filter XML element contains a CALDAV:is-not- + defined XML element and no property of the type specified by + the "name" attribute exists in the enclosing calendar + component; + + or: + + * The CALDAV:prop-filter XML element contains a CALDAV:time-range + XML element and the property value overlaps the specified time + range, and all specified CALDAV:param-filter child XML elements + also match the targeted property; + + or: + + * The CALDAV:prop-filter XML element contains a CALDAV:text-match + XML element and the property value matches it, and all + specified CALDAV:param-filter child XML elements also match the + targeted property; + + Definition: + + + + + name value: a calendar property name (e.g., ATTENDEE) + +9.7.3. CALDAV:param-filter XML Element + + Name: param-filter + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Limits the search to specific parameter values. + + Description: The CALDAV:param-filter XML element specifies a query + targeted at a specific calendar property parameter (e.g., + PARTSTAT) in the scope of the calendar property on which it is + + + +Daboo, et al. Standards Track [Page 87] + +RFC 4791 CalDAV March 2007 + + + defined. A calendar property parameter is said to match a CALDAV: + param-filter if: + + * The CALDAV:param-filter XML element is empty and a parameter of + the type specified by the "name" attribute exists on the + calendar property being examined; + + or: + + * The CALDAV:param-filter XML element contains a CALDAV:is-not- + defined XML element and no parameter of the type specified by + the "name" attribute exists on the calendar property being + examined; + + Definition: + + + + + name value: a property parameter name (e.g., PARTSTAT) + +9.7.4. CALDAV:is-not-defined XML Element + + Name: is-not-defined + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies that a match should occur if the enclosing + component, property, or parameter does not exist. + + Description: The CALDAV:is-not-defined XML element specifies that a + match occurs if the enclosing component, property, or parameter + value specified in a calendaring REPORT request does not exist in + the calendar data being tested. + + Definition: + + + +9.7.5. CALDAV:text-match XML Element + + Name: text-match + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a substring match on a property or parameter + value. + + + + +Daboo, et al. Standards Track [Page 88] + +RFC 4791 CalDAV March 2007 + + + Description: The CALDAV:text-match XML element specifies text used + for a substring match against the property or parameter value + specified in a calendaring REPORT request. + + The "collation" attribute is used to select the collation that the + server MUST use for character string matching. In the absence of + this attribute, the server MUST use the "i;ascii-casemap" + collation. + + The "negate-condition" attribute is used to indicate that this + test returns a match if the text matches when the attribute value + is set to "no", or return a match if the text does not match, if + the attribute value is set to "yes". For example, this can be + used to match components with a STATUS property not set to + CANCELLED. + + Definition: + + + PCDATA value: string + + + +9.8. CALDAV:timezone XML Element + + Name: timezone + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies the time zone component to use when determining + the results of a report. + + Description: The CALDAV:timezone XML element specifies that for a + given calendaring REPORT request, the server MUST rely on the + specified VTIMEZONE component instead of the CALDAV:calendar- + timezone property of the calendar collection, in which the + calendar object resource is contained to resolve "date" values and + "date with local time" values (i.e., floating time) to "date with + UTC time" values. The server will require this information to + determine if a calendar component scheduled with "date" values or + "date with local time" values intersects a CALDAV:time-range + specified in a CALDAV:calendar-query REPORT. + + Note: The iCalendar data embedded within the CALDAV:timezone XML + element MUST follow the standard XML character data encoding + rules, including use of <, >, & etc. entity encoding or + the use of a construct. In the later case, the + + + +Daboo, et al. Standards Track [Page 89] + +RFC 4791 CalDAV March 2007 + + + iCalendar data cannot contain the character sequence "]]>", which + is the end delimiter for the CDATA section. + + Definition: + + + PCDATA value: an iCalendar object with exactly one VTIMEZONE + +9.9. CALDAV:time-range XML Element + + Name: time-range + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: Specifies a time range to limit the set of calendar + components returned by the server. + + Description: The CALDAV:time-range XML element specifies that for a + given calendaring REPORT request, the server MUST only return the + calendar object resources that, depending on the context, have a + component or property whose value intersects a specified time + range. + + The "start" attribute specifies the inclusive start of the time + range, and the "end" attribute specifies the non-inclusive end of + the time range. Both attributes MUST be specified as "date with + UTC time" value. Time ranges open at one end can be specified by + including only one attribute; however, at least one attribute MUST + always be present in the CALDAV:time-range element. If either the + "start" or "end" attribute is not specified in the CALDAV:time- + range XML element, assume "-infinity" and "+infinity" as their + value, respectively. If both "start" and "end" are present, the + value of the "end" attribute MUST be greater than the value of the + "start" attribute. + + Time range tests MUST consider every recurrence instance when + testing the time range condition; if any one instance matches, + then the test returns true. Testing recurrence instances requires + the server to infer an effective value for DTSTART, DTEND, + DURATION, and DUE properties for an instance based on the + recurrence patterns and any overrides. + + A VEVENT component overlaps a given time range if the condition + for the corresponding component state specified in the table below + is satisfied. Note that, as specified in [RFC2445], the DTSTART + property is REQUIRED in the VEVENT component. The conditions + depend on the presence of the DTEND and DURATION properties in the + VEVENT component. Furthermore, the value of the DTEND property + + + +Daboo, et al. Standards Track [Page 90] + +RFC 4791 CalDAV March 2007 + + + MUST be later in time than the value of the DTSTART property. The + duration of a VEVENT component with no DTEND and DURATION + properties is 1 day (+P1D) when the DTSTART is a DATE value, and 0 + seconds when the DTSTART is a DATE-TIME value. + + +---------------------------------------------------------------+ + | VEVENT has the DTEND property? | + | +-----------------------------------------------------------+ + | | VEVENT has the DURATION property? | + | | +-------------------------------------------------------+ + | | | DURATION property value is greater than 0 seconds? | + | | | +---------------------------------------------------+ + | | | | DTSTART property is a DATE-TIME value? | + | | | | +-----------------------------------------------+ + | | | | | Condition to evaluate | + +---+---+---+---+-----------------------------------------------+ + | Y | N | N | * | (start < DTEND AND end > DTSTART) | + +---+---+---+---+-----------------------------------------------+ + | N | Y | Y | * | (start < DTSTART+DURATION AND end > DTSTART) | + | | +---+---+-----------------------------------------------+ + | | | N | * | (start <= DTSTART AND end > DTSTART) | + +---+---+---+---+-----------------------------------------------+ + | N | N | N | Y | (start <= DTSTART AND end > DTSTART) | + +---+---+---+---+-----------------------------------------------+ + | N | N | N | N | (start < DTSTART+P1D AND end > DTSTART) | + +---+---+---+---+-----------------------------------------------+ + + A VTODO component is said to overlap a given time range if the + condition for the corresponding component state specified in the + table below is satisfied. The conditions depend on the presence + of the DTSTART, DURATION, DUE, COMPLETED, and CREATED properties + in the VTODO component. Note that, as specified in [RFC2445], the + DUE value MUST be a DATE-TIME value equal to or after the DTSTART + value if specified. + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 91] + +RFC 4791 CalDAV March 2007 + + + +-------------------------------------------------------------------+ + | VTODO has the DTSTART property? | + | +---------------------------------------------------------------+ + | | VTODO has the DURATION property? | + | | +-----------------------------------------------------------+ + | | | VTODO has the DUE property? | + | | | +-------------------------------------------------------+ + | | | | VTODO has the COMPLETED property? | + | | | | +---------------------------------------------------+ + | | | | | VTODO has the CREATED property? | + | | | | | +-----------------------------------------------+ + | | | | | | Condition to evaluate | + +---+---+---+---+---+-----------------------------------------------+ + | Y | Y | N | * | * | (start <= DTSTART+DURATION) AND | + | | | | | | ((end > DTSTART) OR | + | | | | | | (end >= DTSTART+DURATION)) | + +---+---+---+---+---+-----------------------------------------------+ + | Y | N | Y | * | * | ((start < DUE) OR (start <= DTSTART)) | + | | | | | | AND | + | | | | | | ((end > DTSTART) OR (end >= DUE)) | + +---+---+---+---+---+-----------------------------------------------+ + | Y | N | N | * | * | (start <= DTSTART) AND (end > DTSTART) | + +---+---+---+---+---+-----------------------------------------------+ + | N | N | Y | * | * | (start < DUE) AND (end >= DUE) | + +---+---+---+---+---+-----------------------------------------------+ + | N | N | N | Y | Y | ((start <= CREATED) OR (start <= COMPLETED))| + | | | | | | AND | + | | | | | | ((end >= CREATED) OR (end >= COMPLETED))| + +---+---+---+---+---+-----------------------------------------------+ + | N | N | N | Y | N | (start <= COMPLETED) AND (end >= COMPLETED) | + +---+---+---+---+---+-----------------------------------------------+ + | N | N | N | N | Y | (end > CREATED) | + +---+---+---+---+---+-----------------------------------------------+ + | N | N | N | N | N | TRUE | + +---+---+---+---+---+-----------------------------------------------+ + + A VJOURNAL component overlaps a given time range if the condition + for the corresponding component state specified in the table below + is satisfied. The conditions depend on the presence of the + DTSTART property in the VJOURNAL component and on whether the + DTSTART is a DATE-TIME or DATE value. The effective "duration" of + a VJOURNAL component is 1 day (+P1D) when the DTSTART is a DATE + value, and 0 seconds when the DTSTART is a DATE-TIME value. + + + + + + + + +Daboo, et al. Standards Track [Page 92] + +RFC 4791 CalDAV March 2007 + + + +----------------------------------------------------+ + | VJOURNAL has the DTSTART property? | + | +------------------------------------------------+ + | | DTSTART property is a DATE-TIME value? | + | | +--------------------------------------------+ + | | | Condition to evaluate | + +---+---+--------------------------------------------+ + | Y | Y | (start <= DTSTART) AND (end > DTSTART) | + +---+---+--------------------------------------------+ + | Y | N | (start < DTSTART+P1D) AND (end > DTSTART) | + +---+---+--------------------------------------------+ + | N | * | FALSE | + +---+---+--------------------------------------------+ + + A VFREEBUSY component overlaps a given time range if the condition + for the corresponding component state specified in the table below + is satisfied. The conditions depend on the presence in the + VFREEBUSY component of the DTSTART and DTEND properties, and any + FREEBUSY properties in the absence of DTSTART and DTEND. Any + DURATION property is ignored, as it has a special meaning when + used in a VFREEBUSY component. + + When only FREEBUSY properties are used, each period in each + FREEBUSY property is compared against the time range, irrespective + of the type of free busy information (free, busy, busy-tentative, + busy-unavailable) represented by the property. + + + +------------------------------------------------------+ + | VFREEBUSY has both the DTSTART and DTEND properties? | + | +--------------------------------------------------+ + | | VFREEBUSY has the FREEBUSY property? | + | | +----------------------------------------------+ + | | | Condition to evaluate | + +---+---+----------------------------------------------+ + | Y | * | (start <= DTEND) AND (end > DTSTART) | + +---+---+----------------------------------------------+ + | N | Y | (start < freebusy-period-end) AND | + | | | (end > freebusy-period-start) | + +---+---+----------------------------------------------+ + | N | N | FALSE | + +---+---+----------------------------------------------+ + + A VALARM component is said to overlap a given time range if the + following condition holds: + + (start <= trigger-time) AND (end > trigger-time) + + + + +Daboo, et al. Standards Track [Page 93] + +RFC 4791 CalDAV March 2007 + + + A VALARM component can be defined such that it triggers repeatedly. + Such a VALARM component is said to overlap a given time range if at + least one of its triggers overlaps the time range. + + The calendar properties COMPLETED, CREATED, DTEND, DTSTAMP, + DTSTART, DUE, and LAST-MODIFIED overlap a given time range if the + following condition holds: + + (start <= date-time) AND (end > date-time) + + Note that if DTEND is not present in a VEVENT, but DURATION is, then + the test should instead operate on the 'effective' DTEND, i.e., + DTSTART+DURATION. Similarly, if DUE is not present in a VTODO, but + DTSTART and DURATION are, then the test should instead operate on the + 'effective' DUE, i.e., DTSTART+DURATION. + + The semantic of CALDAV:time-range is not defined for any other + calendar components and properties. + + Definition: + + + + + start value: an iCalendar "date with UTC time" + end value: an iCalendar "date with UTC time" + +9.10. CALDAV:calendar-multiget XML Element + + Name: calendar-multiget + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: CalDAV report used to retrieve specific calendar object + resources. + + Description: See Section 7.9. + + Definition: + + + + + + + + + +Daboo, et al. Standards Track [Page 94] + +RFC 4791 CalDAV March 2007 + + +9.11. CALDAV:free-busy-query XML Element + + Name: free-busy-query + + Namespace: urn:ietf:params:xml:ns:caldav + + Purpose: CalDAV report used to generate a VFREEBUSY to determine + busy time over a specific time range. + + Description: See Section 7.10. + + Definition: + + + +10. Internationalization Considerations + + CalDAV allows internationalized strings to be stored and retrieved + for the description of calendar collections (see Section 5.2.1). + + The CALDAV:calendar-query REPORT (Section 7.8) includes a text + searching option controlled by the CALDAV:text-match element, and + details of character handling are covered in the description of that + element (see Section 9.7.5). + +11. Security Considerations + + HTTP protocol transactions are sent in the clear over the network + unless protection from snooping is negotiated. This can be + accomplished by use of TLS, as defined in [RFC2818]. In particular, + HTTP Basic authentication MUST NOT be used unless TLS is in effect. + + Servers MUST take adequate precautions to ensure that malicious + clients cannot consume excessive server resources (CPU, memory, disk, + etc.) through carefully crafted reports. For example, a client could + upload an event with a recurrence rule that specifies a recurring + event occurring every second for the next 100 years, which would + result in approximately 3 x 10^9 instances! A report that asks for + recurrences to be expanded over that range would likely constitute a + denial-of-service attack on the server. + + When creating new resources (including calendar collections), clients + MUST ensure that the resource name (the last path segment of the + resource URI) assigned to the new resource does not expose any data + from within the iCalendar resource itself or information about the + nature of a calendar collection. This is required to ensure that the + presence of a specific iCalendar component or nature of components in + a collection cannot be inferred based on the name of a resource. + + + +Daboo, et al. Standards Track [Page 95] + +RFC 4791 CalDAV March 2007 + + + When rolling up free-busy information, more information about a + user's events is exposed if busy periods overlap or are adjacent + (this tells the client requesting the free-busy information that the + calendar owner has at least two events, rather than knowing only that + the calendar owner has one or more events during the busy period). + Thus, a conservative approach to calendar data privacy would have + servers always coalesce such busy periods when they are the same + type. + + Procedure alarms are a known security risk for either clients or + servers to handle, particularly when the alarm was created by another + agent. Clients and servers are not required to execute such + procedure alarms. + + Security considerations described in iCalendar [RFC2445] and iTIP + [RFC2446] are also applicable to CalDAV. + + Beyond these, CalDAV does not raise any security considerations that + are not present in HTTP [RFC2616] and WebDAV [RFC2518], [RFC3253], + [RFC3744]. + +12. IANA Considerations + + This document uses one new URN to identify a new XML namespace. The + URN conforms to a registry mechanism described in [RFC3688]. + +12.1. Namespace Registration + + Registration request for the CalDAV namespace: + + URI: urn:ietf:params:xml:ns:caldav + + Registrant Contact: See the "Authors' Addresses" section of this + document. + + XML: None. Namespace URIs do not represent an XML specification. + +13. Acknowledgements + + The authors would like to thank the following individuals for + contributing their ideas and support for writing this specification: + Michael Arick, Mario Bonin, Chris Bryant, Scott Carr, Andre + Courtemanche, Mike Douglass, Ted Hardie, Marten den Haring, Jeffrey + Harris, Sam Hartman, Helge Hess, Jeff McCullough, Alexey Melnikov, + Dan Mosedale, Brian Moseley, Francois Perrault, Kervin L. Pierre, + Julian F. Reschke, Wilfredo Sanchez Vega, Mike Shaver, Jari + Urpalainen, Simon Vaillancourt, and Jim Whitehead. + + + + +Daboo, et al. Standards Track [Page 96] + +RFC 4791 CalDAV March 2007 + + + The authors would also like to thank the Calendaring and Scheduling + Consortium for advice with this specification, and for organizing + interoperability testing events to help refine it. + +14. References + +14.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to + Indicate Requirement Levels", BCP 14, + RFC 2119, March 1997. + + [RFC2246] Dierks, T. and C. Allen, "The TLS Protocol + Version 1.0", RFC 2246, January 1999. + + [RFC2445] Dawson, F. and Stenerson, D., "Internet + Calendaring and Scheduling Core Object + Specification (iCalendar)", RFC 2445, + November 1998. + + [RFC2446] Silverberg, S., Mansour, S., Dawson, F., and + R. Hopson, "iCalendar Transport-Independent + Interoperability Protocol (iTIP) Scheduling + Events, BusyTime, To-dos and Journal + Entries", RFC 2446, November 1998. + + [RFC2518] Goland, Y., Whitehead, E., Faizi, A., Carter, + S., and D. Jensen, "HTTP Extensions for + Distributed Authoring -- WEBDAV", RFC 2518, + February 1999. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, + H., Masinter, L., Leach, P., and T. Berners- + Lee, "Hypertext Transfer Protocol -- + HTTP/1.1", RFC 2616, June 1999. + + [RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, + May 2000. + + [RFC3253] Clemm, G., Amsden, J., Ellison, T., Kaler, + C., and J. Whitehead, "Versioning Extensions + to WebDAV (Web Distributed Authoring and + Versioning)", RFC 3253, March 2002. + + [RFC3688] Mealling, M., "The IETF XML Registry", + BCP 81, RFC 3688, January 2004. + + + + + +Daboo, et al. Standards Track [Page 97] + +RFC 4791 CalDAV March 2007 + + + [RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. + Whitehead, "Web Distributed Authoring and + Versioning (WebDAV) Access Control Protocol", + RFC 3744, May 2004. + + [RFC4346] Dierks, T. and E. Rescorla, "The Transport + Layer Security (TLS) Protocol Version 1.1", + RFC 4346, April 2006. + + [RFC4790] Newman, C., Duerst, M., and A. Gulbrandsen, + "Internet Application Protocol Collation + Registry", RFC 4790, March 2007. + + [W3C.REC-xml-20060816] Paoli, J., Maler, E., Yergeau, F., Sperberg- + McQueen, C., and T. Bray, "Extensible Markup + Language (XML) 1.0 (Fourth Edition)", World + Wide Web Consortium Recommendation REC-xml- + 20060816, August 2006, + . + +14.2. Informative References + + [RFC2426] Dawson, F. and T. Howes, "vCard MIME + Directory Profile", RFC 2426, September 1998. + + [RFC2739] Small, T., Hennessy, D., and F. Dawson, + "Calendar Attributes for vCard and LDAP", + RFC 2739, January 2000. + + [RFC4331] Korver, B. and L. Dusseault, "Quota and Size + Properties for Distributed Authoring and + Versioning (DAV) Collections", RFC 4331, + February 2006. + + [RFC4511] Sermersheim, J., "Lightweight Directory + Access Protocol (LDAP): The Protocol", + RFC 4511, June 2006. + + [rfc2518bis] Dusseault, L., "HTTP Extensions for + Distributed Authoring - WebDAV", Work + in Progress, December 2006. + + + + + + + + + + +Daboo, et al. Standards Track [Page 98] + +RFC 4791 CalDAV March 2007 + + +Appendix A. CalDAV Method Privilege Table (Normative) + + The following table extends the WebDAV Method Privilege Table + specified in Appendix B of [RFC3744]. + + +------------+------------------------------------------------------+ + | METHOD | PRIVILEGES | + +------------+------------------------------------------------------+ + | MKCALENDAR | DAV:bind | + | REPORT | DAV:read or CALDAV:read-free-busy (on all referenced | + | | resources) | + +------------+------------------------------------------------------+ + +Appendix B. Calendar Collections Used in the Examples + + This appendix shows the calendar object resources contained in the + calendar collection queried in the examples throughout this document. + + The content of the calendar collection is being shown as if it were + returned by a CALDAV:calendar-query REPORT request designed to return + all the calendar data in the collection: + + >> Request << + + REPORT /bernard/work/ HTTP/1.1 + Host: cal.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + +Daboo, et al. Standards Track [Page 99] + +RFC 4791 CalDAV March 2007 + + + + + + + http://cal.example.com/bernard/work/abcd1.ics + + + "fffff-abcd1" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTAMP:20060206T001102Z + DTSTART;TZID=US/Eastern:20060102T100000 + DURATION:PT1H + SUMMARY:Event #1 + Description:Go Steelers! + UID:74855313FA803DA593CD579A@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + http://cal.example.com/bernard/work/abcd2.ics + + + + +Daboo, et al. Standards Track [Page 100] + +RFC 4791 CalDAV March 2007 + + + + "fffff-abcd2" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060102T120000 + DURATION:PT1H + RRULE:FREQ=DAILY;COUNT=5 + SUMMARY:Event #2 + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + BEGIN:VEVENT + DTSTAMP:20060206T001121Z + DTSTART;TZID=US/Eastern:20060104T140000 + DURATION:PT1H + RECURRENCE-ID;TZID=US/Eastern:20060104T120000 + SUMMARY:Event #2 bis + UID:00959BC664CA650E933C892C@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + http://cal.example.com/bernard/work/abcd3.ics + + + +Daboo, et al. Standards Track [Page 101] + +RFC 4791 CalDAV March 2007 + + + + + "fffff-abcd3" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTIMEZONE + LAST-MODIFIED:20040110T032845Z + TZID:US/Eastern + BEGIN:DAYLIGHT + DTSTART:20000404T020000 + RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 + TZNAME:EDT + TZOFFSETFROM:-0500 + TZOFFSETTO:-0400 + END:DAYLIGHT + BEGIN:STANDARD + DTSTART:20001026T020000 + RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 + TZNAME:EST + TZOFFSETFROM:-0400 + TZOFFSETTO:-0500 + END:STANDARD + END:VTIMEZONE + BEGIN:VEVENT + ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com + ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com + DTSTAMP:20060206T001220Z + DTSTART;TZID=US/Eastern:20060104T100000 + DURATION:PT1H + LAST-MODIFIED:20060206T001330Z + ORGANIZER:mailto:cyrus@example.com + SEQUENCE:1 + STATUS:TENTATIVE + SUMMARY:Event #3 + UID:DC6C50A017428C5216A2F1CD@example.com + END:VEVENT + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + http://cal.example.com/bernard/work/abcd4.ics + + + + + +Daboo, et al. Standards Track [Page 102] + +RFC 4791 CalDAV March 2007 + + + "fffff-abcd4" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTODO + DTSTAMP:20060205T235335Z + DUE;VALUE=DATE:20060104 + STATUS:NEEDS-ACTION + SUMMARY:Task #1 + UID:DDDEEB7915FA61233B861457@example.com + BEGIN:VALARM + ACTION:AUDIO + TRIGGER;RELATED=START:-PT10M + END:VALARM + END:VTODO + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + http://cal.example.com/bernard/work/abcd5.ics + + + "fffff-abcd5" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTODO + DTSTAMP:20060205T235300Z + DUE;VALUE=DATE:20060106 + LAST-MODIFIED:20060205T235308Z + SEQUENCE:1 + STATUS:NEEDS-ACTION + SUMMARY:Task #2 + UID:E10BA47467C5C69BB74E8720@example.com + BEGIN:VALARM + ACTION:AUDIO + TRIGGER;RELATED=START:-PT10M + END:VALARM + END:VTODO + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + +Daboo, et al. Standards Track [Page 103] + +RFC 4791 CalDAV March 2007 + + + + + + http://cal.example.com/bernard/work/abcd6.ics + + + "fffff-abcd6" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTODO + COMPLETED:20051223T122322Z + DTSTAMP:20060205T235400Z + DUE;VALUE=DATE:20051225 + LAST-MODIFIED:20060205T235308Z + SEQUENCE:1 + STATUS:COMPLETED + SUMMARY:Task #3 + UID:E10BA47467C5C69BB74E8722@example.com + END:VTODO + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + http://cal.example.com/bernard/work/abcd7.ics + + + "fffff-abcd7" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VTODO + DTSTAMP:20060205T235600Z + DUE;VALUE=DATE:20060101 + LAST-MODIFIED:20060205T235308Z + SEQUENCE:1 + STATUS:CANCELLED + SUMMARY:Task #4 + UID:E10BA47467C5C69BB74E8725@example.com + END:VTODO + END:VCALENDAR + + + HTTP/1.1 200 OK + + + +Daboo, et al. Standards Track [Page 104] + +RFC 4791 CalDAV March 2007 + + + + + + + http://cal.example.com/bernard/work/abcd8.ics + + + "fffff-abcd8" + BEGIN:VCALENDAR + VERSION:2.0 + PRODID:-//Example Corp.//CalDAV Client//EN + BEGIN:VFREEBUSY + ORGANIZER;CN="Bernard Desruisseaux":mailto:bernard@example.com + UID:76ef34-54a3d2@example.com + DTSTAMP:20050530T123421Z + DTSTART:20060101T000000Z + DTEND:20060108T000000Z + FREEBUSY:20050531T230000Z/20050601T010000Z + FREEBUSY;FBTYPE=BUSY-TENTATIVE:20060102T100000Z/20060102T120000Z + FREEBUSY:20060103T100000Z/20060103T120000Z + FREEBUSY:20060104T100000Z/20060104T120000Z + FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20060105T100000Z/20060105T120000Z + FREEBUSY:20060106T100000Z/20060106T120000Z + END:VFREEBUSY + END:VCALENDAR + + + HTTP/1.1 200 OK + + + + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 105] + +RFC 4791 CalDAV March 2007 + + +Authors' Addresses + + Cyrus Daboo + Apple Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + EMail: cyrus@daboo.name + URI: http://www.apple.com/ + + + Bernard Desruisseaux + Oracle Corporation + 600 Blvd. de Maisonneuve West + Suite 1900 + Montreal, QC H3A 3J2 + CANADA + + EMail: bernard.desruisseaux@oracle.com + URI: http://www.oracle.com/ + + + Lisa Dusseault + CommerceNet + 169 University Ave. + Palo Alto, CA 94301 + USA + + EMail: ldusseault@commerce.net + URI: http://commerce.net/ + + + + + + + + + + + + + + + + + + + + +Daboo, et al. Standards Track [Page 106] + +RFC 4791 CalDAV March 2007 + + +Full Copyright Statement + + Copyright (C) The IETF Trust (2007). + + This document is subject to the rights, licenses and restrictions + contained in BCP 78, and except as set forth therein, the authors + retain all their rights. + + This document and the information contained herein are provided on an + "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS + OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND + THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF + THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Intellectual Property + + The IETF takes no position regarding the validity or scope of any + Intellectual Property Rights or other rights that might be claimed to + pertain to the implementation or use of the technology described in + this document or the extent to which any license under such rights + might or might not be available; nor does it represent that it has + made any independent effort to identify any such rights. Information + on the procedures with respect to rights in RFC documents can be + found in BCP 78 and BCP 79. + + Copies of IPR disclosures made to the IETF Secretariat and any + assurances of licenses to be made available, or the result of an + attempt made to obtain a general license or permission for the use of + such proprietary rights by implementers or users of this + specification can be obtained from the IETF on-line IPR repository at + http://www.ietf.org/ipr. + + The IETF invites any interested party to bring to its attention any + copyrights, patents or patent applications, or other proprietary + rights that may cover technology that may be required to implement + this standard. Please address the information to the IETF at + ietf-ipr@ietf.org. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + +Daboo, et al. Standards Track [Page 107] + diff --git a/doc/rfc4918-webdav.txt b/doc/rfc4918-webdav.txt new file mode 100644 index 00000000..4ef181bb --- /dev/null +++ b/doc/rfc4918-webdav.txt @@ -0,0 +1,7115 @@ + + + + + + +Network Working Group L. Dusseault, Ed. +Request for Comments: 4918 CommerceNet +Obsoletes: 2518 June 2007 +Category: Standards Track + + + HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV) + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The IETF Trust (2007). + +Abstract + + Web Distributed Authoring and Versioning (WebDAV) consists of a set + of methods, headers, and content-types ancillary to HTTP/1.1 for the + management of resource properties, creation and management of + resource collections, URL namespace manipulation, and resource + locking (collision avoidance). + + RFC 2518 was published in February 1999, and this specification + obsoletes RFC 2518 with minor revisions mostly due to + interoperability experience. + + + + + + + + + + + + + + + + + + + + +Dusseault Standards Track [Page 1] + +RFC 4918 WebDAV June 2007 + + +Table of Contents + + 1. Introduction ....................................................7 + 2. Notational Conventions ..........................................8 + 3. Terminology .....................................................8 + 4. Data Model for Resource Properties .............................10 + 4.1. The Resource Property Model ...............................10 + 4.2. Properties and HTTP Headers ...............................10 + 4.3. Property Values ...........................................10 + 4.3.1. Example - Property with Mixed Content ..............12 + 4.4. Property Names ............................................14 + 4.5. Source Resources and Output Resources .....................14 + 5. Collections of Web Resources ...................................14 + 5.1. HTTP URL Namespace Model ..................................15 + 5.2. Collection Resources ......................................15 + 6. Locking ........................................................17 + 6.1. Lock Model ................................................18 + 6.2. Exclusive vs. Shared Locks ................................19 + 6.3. Required Support ..........................................20 + 6.4. Lock Creator and Privileges ...............................20 + 6.5. Lock Tokens ...............................................21 + 6.6. Lock Timeout ..............................................21 + 6.7. Lock Capability Discovery .................................22 + 6.8. Active Lock Discovery .....................................22 + 7. Write Lock .....................................................23 + 7.1. Write Locks and Properties ................................24 + 7.2. Avoiding Lost Updates .....................................24 + 7.3. Write Locks and Unmapped URLs .............................25 + 7.4. Write Locks and Collections ...............................26 + 7.5. Write Locks and the If Request Header .....................28 + 7.5.1. Example - Write Lock and COPY ......................28 + 7.5.2. Example - Deleting a Member of a Locked + Collection .........................................29 + 7.6. Write Locks and COPY/MOVE .................................30 + 7.7. Refreshing Write Locks ....................................30 + 8. General Request and Response Handling ..........................31 + 8.1. Precedence in Error Handling ..............................31 + 8.2. Use of XML ................................................31 + 8.3. URL Handling ..............................................32 + 8.3.1. Example - Correct URL Handling .....................32 + 8.4. Required Bodies in Requests ...............................33 + 8.5. HTTP Headers for Use in WebDAV ............................33 + 8.6. ETag ......................................................33 + 8.7. Including Error Response Bodies ...........................34 + 8.8. Impact of Namespace Operations on Cache Validators ........34 + 9. HTTP Methods for Distributed Authoring .........................35 + 9.1. PROPFIND Method ...........................................35 + 9.1.1. PROPFIND Status Codes ..............................37 + + + +Dusseault Standards Track [Page 2] + +RFC 4918 WebDAV June 2007 + + + 9.1.2. Status Codes for Use in 'propstat' Element .........37 + 9.1.3. Example - Retrieving Named Properties ..............38 + 9.1.4. Example - Using 'propname' to Retrieve All + Property Names .....................................39 + 9.1.5. Example - Using So-called 'allprop' ................41 + 9.1.6. Example - Using 'allprop' with 'include' ...........43 + 9.2. PROPPATCH Method ..........................................44 + 9.2.1. Status Codes for Use in 'propstat' Element .........44 + 9.2.2. Example - PROPPATCH ................................45 + 9.3. MKCOL Method ..............................................46 + 9.3.1. MKCOL Status Codes .................................47 + 9.3.2. Example - MKCOL ....................................47 + 9.4. GET, HEAD for Collections .................................48 + 9.5. POST for Collections ......................................48 + 9.6. DELETE Requirements .......................................48 + 9.6.1. DELETE for Collections .............................49 + 9.6.2. Example - DELETE ...................................49 + 9.7. PUT Requirements ..........................................50 + 9.7.1. PUT for Non-Collection Resources ...................50 + 9.7.2. PUT for Collections ................................51 + 9.8. COPY Method ...............................................51 + 9.8.1. COPY for Non-collection Resources ..................51 + 9.8.2. COPY for Properties ................................52 + 9.8.3. COPY for Collections ...............................52 + 9.8.4. COPY and Overwriting Destination Resources .........53 + 9.8.5. Status Codes .......................................54 + 9.8.6. Example - COPY with Overwrite ......................55 + 9.8.7. Example - COPY with No Overwrite ...................55 + 9.8.8. Example - COPY of a Collection .....................56 + 9.9. MOVE Method ...............................................56 + 9.9.1. MOVE for Properties ................................57 + 9.9.2. MOVE for Collections ...............................57 + 9.9.3. MOVE and the Overwrite Header ......................58 + 9.9.4. Status Codes .......................................59 + 9.9.5. Example - MOVE of a Non-Collection .................60 + 9.9.6. Example - MOVE of a Collection .....................60 + 9.10. LOCK Method ..............................................61 + 9.10.1. Creating a Lock on an Existing Resource ...........61 + 9.10.2. Refreshing Locks ..................................62 + 9.10.3. Depth and Locking .................................62 + 9.10.4. Locking Unmapped URLs .............................63 + 9.10.5. Lock Compatibility Table ..........................63 + 9.10.6. LOCK Responses ....................................63 + 9.10.7. Example - Simple Lock Request .....................64 + 9.10.8. Example - Refreshing a Write Lock .................65 + 9.10.9. Example - Multi-Resource Lock Request .............66 + 9.11. UNLOCK Method ............................................68 + 9.11.1. Status Codes ......................................68 + + + +Dusseault Standards Track [Page 3] + +RFC 4918 WebDAV June 2007 + + + 9.11.2. Example - UNLOCK ..................................69 + 10. HTTP Headers for Distributed Authoring ........................69 + 10.1. DAV Header ...............................................69 + 10.2. Depth Header .............................................70 + 10.3. Destination Header .......................................71 + 10.4. If Header ................................................72 + 10.4.1. Purpose ...........................................72 + 10.4.2. Syntax ............................................72 + 10.4.3. List Evaluation ...................................73 + 10.4.4. Matching State Tokens and ETags ...................74 + 10.4.5. If Header and Non-DAV-Aware Proxies ...............74 + 10.4.6. Example - No-tag Production .......................75 + 10.4.7. Example - Using "Not" with No-tag Production ......75 + 10.4.8. Example - Causing a Condition to Always + Evaluate to True ..................................75 + 10.4.9. Example - Tagged List If Header in COPY ...........76 + 10.4.10. Example - Matching Lock Tokens with + Collection Locks .................................76 + 10.4.11. Example - Matching ETags on Unmapped URLs ........76 + 10.5. Lock-Token Header ........................................77 + 10.6. Overwrite Header .........................................77 + 10.7. Timeout Request Header ...................................78 + 11. Status Code Extensions to HTTP/1.1 ............................78 + 11.1. 207 Multi-Status .........................................78 + 11.2. 422 Unprocessable Entity .................................78 + 11.3. 423 Locked ...............................................78 + 11.4. 424 Failed Dependency ....................................79 + 11.5. 507 Insufficient Storage .................................79 + 12. Use of HTTP Status Codes ......................................79 + 12.1. 412 Precondition Failed ..................................79 + 12.2. 414 Request-URI Too Long .................................79 + 13. Multi-Status Response .........................................80 + 13.1. Response Headers .........................................80 + 13.2. Handling Redirected Child Resources ......................81 + 13.3. Internal Status Codes ....................................81 + 14. XML Element Definitions .......................................81 + 14.1. activelock XML Element ...................................81 + 14.2. allprop XML Element ......................................82 + 14.3. collection XML Element ...................................82 + 14.4. depth XML Element ........................................82 + 14.5. error XML Element ........................................82 + 14.6. exclusive XML Element ....................................83 + 14.7. href XML Element .........................................83 + 14.8. include XML Element ......................................83 + 14.9. location XML Element .....................................83 + 14.10. lockentry XML Element ...................................84 + 14.11. lockinfo XML Element ....................................84 + 14.12. lockroot XML Element ....................................84 + + + +Dusseault Standards Track [Page 4] + +RFC 4918 WebDAV June 2007 + + + 14.13. lockscope XML Element ...................................84 + 14.14. locktoken XML Element ...................................85 + 14.15. locktype XML Element ....................................85 + 14.16. multistatus XML Element .................................85 + 14.17. owner XML Element .......................................85 + 14.18. prop XML Element ........................................86 + 14.19. propertyupdate XML Element ..............................86 + 14.20. propfind XML Element ....................................86 + 14.21. propname XML Element ....................................87 + 14.22. propstat XML Element ....................................87 + 14.23. remove XML Element ......................................87 + 14.24. response XML Element ....................................88 + 14.25. responsedescription XML Element .........................88 + 14.26. set XML Element .........................................88 + 14.27. shared XML Element ......................................89 + 14.28. status XML Element ......................................89 + 14.29. timeout XML Element .....................................89 + 14.30. write XML Element .......................................89 + 15. DAV Properties ................................................90 + 16. Precondition/Postcondition XML Elements .......................98 + 17. XML Extensibility in DAV .....................................101 + 18. DAV Compliance Classes .......................................103 + 18.1. Class 1 .................................................103 + 18.2. Class 2 .................................................103 + 18.3. Class 3 .................................................103 + 19. Internationalization Considerations ..........................104 + 20. Security Considerations ......................................105 + 20.1. Authentication of Clients ...............................105 + 20.2. Denial of Service .......................................106 + 20.3. Security through Obscurity ..............................106 + 20.4. Privacy Issues Connected to Locks .......................106 + 20.5. Privacy Issues Connected to Properties ..................107 + 20.6. Implications of XML Entities ............................107 + 20.7. Risks Connected with Lock Tokens ........................108 + 20.8. Hosting Malicious Content ...............................108 + 21. IANA Considerations ..........................................109 + 21.1. New URI Schemes .........................................109 + 21.2. XML Namespaces ..........................................109 + 21.3. Message Header Fields ...................................109 + 21.3.1. DAV ..............................................109 + 21.3.2. Depth ............................................110 + 21.3.3. Destination ......................................110 + 21.3.4. If ...............................................110 + 21.3.5. Lock-Token .......................................110 + 21.3.6. Overwrite ........................................111 + 21.3.7. Timeout ..........................................111 + 21.4. HTTP Status Codes .......................................111 + 22. Acknowledgements .............................................112 + + + +Dusseault Standards Track [Page 5] + +RFC 4918 WebDAV June 2007 + + + 23. Contributors to This Specification ...........................113 + 24. Authors of RFC 2518 ..........................................113 + 25. References ...................................................114 + 25.1. Normative References.....................................114 + 25.2. Informative References ..................................115 + Appendix A. Notes on Processing XML Elements ....................117 + A.1. Notes on Empty XML Elements ..............................117 + A.2. Notes on Illegal XML Processing ..........................117 + A.3. Example - XML Syntax Error ...............................117 + A.4. Example - Unexpected XML Element .........................118 + Appendix B. Notes on HTTP Client Compatibility ...................119 + Appendix C. The 'opaquelocktoken' Scheme and URIs ................120 + Appendix D. Lock-null Resources ..................................120 + D.1. Guidance for Clients Using LOCK to Create Resources ......121 + Appendix E. Guidance for Clients Desiring to Authenticate ........121 + Appendix F. Summary of Changes from RFC 2518 .....................123 + F.1. Changes for Both Client and Server Implementations .......123 + F.2. Changes for Server Implementations .......................125 + F.3. Other Changes ............................................126 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Dusseault Standards Track [Page 6] + +RFC 4918 WebDAV June 2007 + + +1. Introduction + + This document describes an extension to the HTTP/1.1 protocol that + allows clients to perform remote Web content authoring operations. + This extension provides a coherent set of methods, headers, request + entity body formats, and response entity body formats that provide + operations for: + + Properties: The ability to create, remove, and query information + about Web pages, such as their authors, creation dates, etc. + + Collections: The ability to create sets of documents and to retrieve + a hierarchical membership listing (like a directory listing in a file + system). + + Locking: The ability to keep more than one person from working on a + document at the same time. This prevents the "lost update problem", + in which modifications are lost as first one author, then another, + writes changes without merging the other author's changes. + + Namespace Operations: The ability to instruct the server to copy and + move Web resources, operations that change the mapping from URLs to + resources. + + Requirements and rationale for these operations are described in a + companion document, "Requirements for a Distributed Authoring and + Versioning Protocol for the World Wide Web" [RFC2291]. + + This document does not specify the versioning operations suggested by + [RFC2291]. That work was done in a separate document, "Versioning + Extensions to WebDAV" [RFC3253]. + + The sections below provide a detailed introduction to various WebDAV + abstractions: resource properties (Section 4), collections of + resources (Section 5), locks (Section 6) in general, and write locks + (Section 7) specifically. + + These abstractions are manipulated by the WebDAV-specific HTTP + methods (Section 9) and the extra HTTP headers (Section 10) used with + WebDAV methods. General considerations for handling HTTP requests + and responses in WebDAV are found in Section 8. + + While the status codes provided by HTTP/1.1 are sufficient to + describe most error conditions encountered by WebDAV methods, there + are some errors that do not fall neatly into the existing categories. + This specification defines extra status codes developed for WebDAV + methods (Section 11) and describes existing HTTP status codes + (Section 12) as used in WebDAV. Since some WebDAV methods may + + + +Dusseault Standards Track [Page 7] + +RFC 4918 WebDAV June 2007 + + + operate over many resources, the Multi-Status response (Section 13) + has been introduced to return status information for multiple + resources. Finally, this version of WebDAV introduces precondition + and postcondition (Section 16) XML elements in error response bodies. + + WebDAV uses XML ([REC-XML]) for property names and some values, and + also uses XML to marshal complicated requests and responses. This + specification contains DTD and text definitions of all properties + (Section 15) and all other XML elements (Section 14) used in + marshalling. WebDAV includes a few special rules on extending WebDAV + XML marshalling in backwards-compatible ways (Section 17). + + Finishing off the specification are sections on what it means for a + resource to be compliant with this specification (Section 18), on + internationalization support (Section 19), and on security + (Section 20). + +2. Notational Conventions + + Since this document describes a set of extensions to the HTTP/1.1 + protocol, the augmented BNF used herein to describe protocol elements + is exactly the same as described in Section 2.1 of [RFC2616], + including the rules about implied linear whitespace. Since this + augmented BNF uses the basic production rules provided in Section 2.2 + of [RFC2616], these rules apply to this document as well. Note this + is not the standard BNF syntax used in other RFCs. + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + Note that in natural language, a property like the "creationdate" + property in the "DAV:" XML namespace is sometimes referred to as + "DAV:creationdate" for brevity. + +3. Terminology + + URI/URL - A Uniform Resource Identifier and Uniform Resource Locator, + respectively. These terms (and the distinction between them) are + defined in [RFC3986]. + + URI/URL Mapping - A relation between an absolute URI and a resource. + Since a resource can represent items that are not network + retrievable, as well as those that are, it is possible for a resource + to have zero, one, or many URI mappings. Mapping a resource to an + "http" scheme URI makes it possible to submit HTTP protocol requests + to the resource using the URI. + + + + +Dusseault Standards Track [Page 8] + +RFC 4918 WebDAV June 2007 + + + Path Segment - Informally, the characters found between slashes ("/") + in a URI. Formally, as defined in Section 3.3 of [RFC3986]. + + Collection - Informally, a resource that also acts as a container of + references to child resources. Formally, a resource that contains a + set of mappings between path segments and resources and meets the + requirements defined in Section 5. + + Internal Member (of a Collection) - Informally, a child resource of a + collection. Formally, a resource referenced by a path segment + mapping contained in the collection. + + Internal Member URL (of a Collection) - A URL of an internal member, + consisting of the URL of the collection (including trailing slash) + plus the path segment identifying the internal member. + + Member (of a Collection) - Informally, a "descendant" of a + collection. Formally, an internal member of the collection, or, + recursively, a member of an internal member. + + Member URL (of a Collection) - A URL that is either an internal + member URL of the collection itself, or is an internal member URL of + a member of that collection. + + Property - A name/value pair that contains descriptive information + about a resource. + + Live Property - A property whose semantics and syntax are enforced by + the server. For example, the live property DAV:getcontentlength has + its value, the length of the entity returned by a GET request, + automatically calculated by the server. + + Dead Property - A property whose semantics and syntax are not + enforced by the server. The server only records the value of a dead + property; the client is responsible for maintaining the consistency + of the syntax and semantics of a dead property. + + Principal - A distinct human or computational actor that initiates + access to network resources. + + State Token - A URI that represents a state of a resource. Lock + tokens are the only state tokens defined in this specification. + + + + + + + + + +Dusseault Standards Track [Page 9] + +RFC 4918 WebDAV June 2007 + + +4. Data Model for Resource Properties + +4.1. The Resource Property Model + + Properties are pieces of data that describe the state of a resource. + Properties are data about data. + + Properties are used in distributed authoring environments to provide + for efficient discovery and management of resources. For example, a + 'subject' property might allow for the indexing of all resources by + their subject, and an 'author' property might allow for the discovery + of what authors have written which documents. + + The DAV property model consists of name/value pairs. The name of a + property identifies the property's syntax and semantics, and provides + an address by which to refer to its syntax and semantics. + + There are two categories of properties: "live" and "dead". A live + property has its syntax and semantics enforced by the server. Live + properties include cases where a) the value of a property is + protected and maintained by the server, and b) the value of the + property is maintained by the client, but the server performs syntax + checking on submitted values. All instances of a given live property + MUST comply with the definition associated with that property name. + A dead property has its syntax and semantics enforced by the client; + the server merely records the value of the property verbatim. + +4.2. Properties and HTTP Headers + + Properties already exist, in a limited sense, in HTTP message + headers. However, in distributed authoring environments, a + relatively large number of properties are needed to describe the + state of a resource, and setting/returning them all through HTTP + headers is inefficient. Thus, a mechanism is needed that allows a + principal to identify a set of properties in which the principal is + interested and to set or retrieve just those properties. + +4.3. Property Values + + The value of a property is always a (well-formed) XML fragment. + + XML has been chosen because it is a flexible, self-describing, + structured data format that supports rich schema definitions, and + because of its support for multiple character sets. XML's self- + describing nature allows any property's value to be extended by + adding elements. Clients will not break when they encounter + extensions because they will still have the data specified in the + original schema and MUST ignore elements they do not understand. + + + +Dusseault Standards Track [Page 10] + +RFC 4918 WebDAV June 2007 + + + XML's support for multiple character sets allows any human-readable + property to be encoded and read in a character set familiar to the + user. XML's support for multiple human languages, using the "xml: + lang" attribute, handles cases where the same character set is + employed by multiple human languages. Note that xml:lang scope is + recursive, so an xml:lang attribute on any element containing a + property name element applies to the property value unless it has + been overridden by a more locally scoped attribute. Note that a + property only has one value, in one language (or language MAY be left + undefined); a property does not have multiple values in different + languages or a single value in multiple languages. + + A property is always represented with an XML element consisting of + the property name, called the "property name element". The simplest + example is an empty property, which is different from a property that + does not exist: + + + + The value of the property appears inside the property name element. + The value may be any kind of well-formed XML content, including both + text-only and mixed content. Servers MUST preserve the following XML + Information Items (using the terminology from [REC-XML-INFOSET]) in + storage and transmission of dead properties: + + For the property name Element Information Item itself: + + [namespace name] + + [local name] + + [attributes] named "xml:lang" or any such attribute in scope + + [children] of type element or character + + On all Element Information Items in the property value: + + [namespace name] + + [local name] + + [attributes] + + [children] of type element or character + + + + + + + +Dusseault Standards Track [Page 11] + +RFC 4918 WebDAV June 2007 + + + On Attribute Information Items in the property value: + + [namespace name] + + [local name] + + [normalized value] + + On Character Information Items in the property value: + + [character code] + + Since prefixes are used in some XML vocabularies (XPath and XML + Schema, for example), servers SHOULD preserve, for any Information + Item in the value: + + [prefix] + + XML Infoset attributes not listed above MAY be preserved by the + server, but clients MUST NOT rely on them being preserved. The above + rules would also apply by default to live properties, unless defined + otherwise. + + Servers MUST ignore the XML attribute xml:space if present and never + use it to change whitespace handling. Whitespace in property values + is significant. + +4.3.1. Example - Property with Mixed Content + + Consider a dead property 'author' created by the client as follows: + + + + Jane Doe + + mailto:jane.doe@example.com + http://www.example.com + + Jane has been working way too long on the + long-awaited revision of ]]>. + + + + + + + + + +Dusseault Standards Track [Page 12] + +RFC 4918 WebDAV June 2007 + + + When this property is requested, a server might return: + + + Jane Doe + mailto:jane.doe@example.com + http://www.example.com + + Jane has been working way too long on the + long-awaited revision of <RFC2518>. + + + + + Note in this example: + + o The [prefix] for the property name itself was not preserved, being + non-significant, whereas all other [prefix] values have been + preserved, + + o attribute values have been rewritten with double quotes instead of + single quotes (quoting style is not significant), and attribute + order has not been preserved, + + o the xml:lang attribute has been returned on the property name + element itself (it was in scope when the property was set, but the + exact position in the response is not considered significant as + long as it is in scope), + + o whitespace between tags has been preserved everywhere (whitespace + between attributes not so), + + o CDATA encapsulation was replaced with character escaping (the + reverse would also be legal), + + o the comment item was stripped (as would have been a processing + instruction item). + + Implementation note: there are cases such as editing scenarios where + clients may require that XML content is preserved character by + character (such as attribute ordering or quoting style). In this + case, clients should consider using a text-only property value by + escaping all characters that have a special meaning in XML parsing. + + + +Dusseault Standards Track [Page 13] + +RFC 4918 WebDAV June 2007 + + +4.4. Property Names + + A property name is a universally unique identifier that is associated + with a schema that provides information about the syntax and + semantics of the property. + + Because a property's name is universally unique, clients can depend + upon consistent behavior for a particular property across multiple + resources, on the same and across different servers, so long as that + property is "live" on the resources in question, and the + implementation of the live property is faithful to its definition. + + The XML namespace mechanism, which is based on URIs ([RFC3986]), is + used to name properties because it prevents namespace collisions and + provides for varying degrees of administrative control. + + The property namespace is flat; that is, no hierarchy of properties + is explicitly recognized. Thus, if a property A and a property A/B + exist on a resource, there is no recognition of any relationship + between the two properties. It is expected that a separate + specification will eventually be produced that will address issues + relating to hierarchical properties. + + Finally, it is not possible to define the same property twice on a + single resource, as this would cause a collision in the resource's + property namespace. + +4.5. Source Resources and Output Resources + + Some HTTP resources are dynamically generated by the server. For + these resources, there presumably exists source code somewhere + governing how that resource is generated. The relationship of source + files to output HTTP resources may be one to one, one to many, many + to one, or many to many. There is no mechanism in HTTP to determine + whether a resource is even dynamic, let alone where its source files + exist or how to author them. Although this problem would usefully be + solved, interoperable WebDAV implementations have been widely + deployed without actually solving this problem, by dealing only with + static resources. Thus, the source vs. output problem is not solved + in this specification and has been deferred to a separate document. + +5. Collections of Web Resources + + This section provides a description of a type of Web resource, the + collection, and discusses its interactions with the HTTP URL + namespace and with HTTP methods. The purpose of a collection + resource is to model collection-like objects (e.g., file system + directories) within a server's namespace. + + + +Dusseault Standards Track [Page 14] + +RFC 4918 WebDAV June 2007 + + + All DAV-compliant resources MUST support the HTTP URL namespace model + specified herein. + +5.1. HTTP URL Namespace Model + + The HTTP URL namespace is a hierarchical namespace where the + hierarchy is delimited with the "/" character. + + An HTTP URL namespace is said to be consistent if it meets the + following conditions: for every URL in the HTTP hierarchy there + exists a collection that contains that URL as an internal member URL. + The root, or top-level collection of the namespace under + consideration, is exempt from the previous rule. The top-level + collection of the namespace under consideration is not necessarily + the collection identified by the absolute path '/' -- it may be + identified by one or more path segments (e.g., /servlets/webdav/...) + + Neither HTTP/1.1 nor WebDAV requires that the entire HTTP URL + namespace be consistent -- a WebDAV-compatible resource may not have + a parent collection. However, certain WebDAV methods are prohibited + from producing results that cause namespace inconsistencies. + + As is implicit in [RFC2616] and [RFC3986], any resource, including + collection resources, MAY be identified by more than one URI. For + example, a resource could be identified by multiple HTTP URLs. + +5.2. Collection Resources + + Collection resources differ from other resources in that they also + act as containers. Some HTTP methods apply only to a collection, but + some apply to some or all of the resources inside the container + defined by the collection. When the scope of a method is not clear, + the client can specify what depth to apply. Depth can be either zero + levels (only the collection), one level (the collection and directly + contained resources), or infinite levels (the collection and all + contained resources recursively). + + A collection's state consists of at least a set of mappings between + path segments and resources, and a set of properties on the + collection itself. In this document, a resource B will be said to be + contained in the collection resource A if there is a path segment + mapping that maps to B and that is contained in A. A collection MUST + contain at most one mapping for a given path segment, i.e., it is + illegal to have the same path segment mapped to more than one + resource. + + + + + + +Dusseault Standards Track [Page 15] + +RFC 4918 WebDAV June 2007 + + + Properties defined on collections behave exactly as do properties on + non-collection resources. A collection MAY have additional state + such as entity bodies returned by GET. + + For all WebDAV-compliant resources A and B, identified by URLs "U" + and "V", respectively, such that "V" is equal to "U/SEGMENT", A MUST + be a collection that contains a mapping from "SEGMENT" to B. So, if + resource B with URL "http://example.com/bar/blah" is WebDAV compliant + and if resource A with URL "http://example.com/bar/" is WebDAV + compliant, then resource A must be a collection and must contain + exactly one mapping from "blah" to B. + + Although commonly a mapping consists of a single segment and a + resource, in general, a mapping consists of a set of segments and a + resource. This allows a server to treat a set of segments as + equivalent (i.e., either all of the segments are mapped to the same + resource, or none of the segments are mapped to a resource). For + example, a server that performs case-folding on segments will treat + the segments "ab", "Ab", "aB", and "AB" as equivalent. A client can + then use any of these segments to identify the resource. Note that a + PROPFIND result will select one of these equivalent segments to + identify the mapping, so there will be one PROPFIND response element + per mapping, not one per segment in the mapping. + + Collection resources MAY have mappings to non-WebDAV-compliant + resources in the HTTP URL namespace hierarchy but are not required to + do so. For example, if resource X with URL + "http://example.com/bar/blah" is not WebDAV compliant and resource A + with "URL http://example.com/bar/" identifies a WebDAV collection, + then A may or may not have a mapping from "blah" to X. + + If a WebDAV-compliant resource has no WebDAV-compliant internal + members in the HTTP URL namespace hierarchy, then the WebDAV- + compliant resource is not required to be a collection. + + There is a standing convention that when a collection is referred to + by its name without a trailing slash, the server MAY handle the + request as if the trailing slash were present. In this case, it + SHOULD return a Content-Location header in the response, pointing to + the URL ending with the "/". For example, if a client invokes a + method on http://example.com/blah (no trailing slash), the server may + respond as if the operation were invoked on http://example.com/blah/ + (trailing slash), and should return a Content-Location header with + the value http://example.com/blah/. Wherever a server produces a URL + referring to a collection, the server SHOULD include the trailing + slash. In general, clients SHOULD use the trailing slash form of + collection names. If clients do not use the trailing slash form the + client needs to be prepared to see a redirect response. Clients will + + + +Dusseault Standards Track [Page 16] + +RFC 4918 WebDAV June 2007 + + + find the DAV:resourcetype property more reliable than the URL to find + out if a resource is a collection. + + Clients MUST be able to support the case where WebDAV resources are + contained inside non-WebDAV resources. For example, if an OPTIONS + response from "http://example.com/servlet/dav/collection" indicates + WebDAV support, the client cannot assume that + "http://example.com/servlet/dav/" or its parent necessarily are + WebDAV collections. + + A typical scenario in which mapped URLs do not appear as members of + their parent collection is the case where a server allows links or + redirects to non-WebDAV resources. For instance, "/col/link" might + not appear as a member of "/col/", although the server would respond + with a 302 status to a GET request to "/col/link"; thus, the URL + "/col/link" would indeed be mapped. Similarly, a dynamically- + generated page might have a URL mapping from "/col/index.html", thus + this resource might respond with a 200 OK to a GET request yet not + appear as a member of "/col/". + + Some mappings to even WebDAV-compliant resources might not appear in + the parent collection. An example for this case are servers that + support multiple alias URLs for each WebDAV-compliant resource. A + server may implement case-insensitive URLs, thus "/col/a" and + "/col/A" identify the same resource, yet only either "a" or "A" is + reported upon listing the members of "/col". In cases where a server + treats a set of segments as equivalent, the server MUST expose only + one preferred segment per mapping, consistently chosen, in PROPFIND + responses. + +6. Locking + + The ability to lock a resource provides a mechanism for serializing + access to that resource. Using a lock, an authoring client can + provide a reasonable guarantee that another principal will not modify + a resource while it is being edited. In this way, a client can + prevent the "lost update" problem. + + This specification allows locks to vary over two client-specified + parameters, the number of principals involved (exclusive vs. shared) + and the type of access to be granted. This document defines locking + for only one access type, write. However, the syntax is extensible, + and permits the eventual specification of locking for other access + types. + + + + + + + +Dusseault Standards Track [Page 17] + +RFC 4918 WebDAV June 2007 + + +6.1. Lock Model + + This section provides a concise model for how locking behaves. Later + sections will provide more detail on some of the concepts and refer + back to these model statements. Normative statements related to LOCK + and UNLOCK method handling can be found in the sections on those + methods, whereas normative statements that cover any method are + gathered here. + + 1. A lock either directly or indirectly locks a resource. + + 2. A resource becomes directly locked when a LOCK request to a URL + of that resource creates a new lock. The "lock-root" of the new + lock is that URL. If at the time of the request, the URL is not + mapped to a resource, a new empty resource is created and + directly locked. + + 3. An exclusive lock (Section 6.2) conflicts with any other kind of + lock on the same resource, whether either lock is direct or + indirect. A server MUST NOT create conflicting locks on a + resource. + + 4. For a collection that is locked with a depth-infinity lock L, all + member resources are indirectly locked. Changes in membership of + such a collection affect the set of indirectly locked resources: + + * If a member resource is added to the collection, the new + member resource MUST NOT already have a conflicting lock, + because the new resource MUST become indirectly locked by L. + + * If a member resource stops being a member of the collection, + then the resource MUST no longer be indirectly locked by L. + + 5. Each lock is identified by a single globally unique lock token + (Section 6.5). + + 6. An UNLOCK request deletes the lock with the specified lock token. + After a lock is deleted, no resource is locked by that lock. + + 7. A lock token is "submitted" in a request when it appears in an + "If" header (Section 7, "Write Lock", discusses when token + submission is required for write locks). + + 8. If a request causes the lock-root of any lock to become an + unmapped URL, then the lock MUST also be deleted by that request. + + + + + + +Dusseault Standards Track [Page 18] + +RFC 4918 WebDAV June 2007 + + +6.2. Exclusive vs. Shared Locks + + The most basic form of lock is an exclusive lock. Exclusive locks + avoid having to deal with content change conflicts, without requiring + any coordination other than the methods described in this + specification. + + However, there are times when the goal of a lock is not to exclude + others from exercising an access right but rather to provide a + mechanism for principals to indicate that they intend to exercise + their access rights. Shared locks are provided for this case. A + shared lock allows multiple principals to receive a lock. Hence any + principal that has both access privileges and a valid lock can use + the locked resource. + + With shared locks, there are two trust sets that affect a resource. + The first trust set is created by access permissions. Principals who + are trusted, for example, may have permission to write to the + resource. Among those who have access permission to write to the + resource, the set of principals who have taken out a shared lock also + must trust each other, creating a (typically) smaller trust set + within the access permission write set. + + Starting with every possible principal on the Internet, in most + situations the vast majority of these principals will not have write + access to a given resource. Of the small number who do have write + access, some principals may decide to guarantee their edits are free + from overwrite conflicts by using exclusive write locks. Others may + decide they trust their collaborators will not overwrite their work + (the potential set of collaborators being the set of principals who + have write permission) and use a shared lock, which informs their + collaborators that a principal may be working on the resource. + + The WebDAV extensions to HTTP do not need to provide all of the + communications paths necessary for principals to coordinate their + activities. When using shared locks, principals may use any out-of- + band communication channel to coordinate their work (e.g., face-to- + face interaction, written notes, post-it notes on the screen, + telephone conversation, email, etc.) The intent of a shared lock is + to let collaborators know who else may be working on a resource. + + Shared locks are included because experience from Web-distributed + authoring systems has indicated that exclusive locks are often too + rigid. An exclusive lock is used to enforce a particular editing + process: take out an exclusive lock, read the resource, perform + edits, write the resource, release the lock. This editing process + has the problem that locks are not always properly released, for + example, when a program crashes or when a lock creator leaves without + + + +Dusseault Standards Track [Page 19] + +RFC 4918 WebDAV June 2007 + + + unlocking a resource. While both timeouts (Section 6.6) and + administrative action can be used to remove an offending lock, + neither mechanism may be available when needed; the timeout may be + long or the administrator may not be available. + + A successful request for a new shared lock MUST result in the + generation of a unique lock associated with the requesting principal. + Thus, if five principals have taken out shared write locks on the + same resource, there will be five locks and five lock tokens, one for + each principal. + +6.3. Required Support + + A WebDAV-compliant resource is not required to support locking in any + form. If the resource does support locking, it may choose to support + any combination of exclusive and shared locks for any access types. + + The reason for this flexibility is that locking policy strikes to the + very heart of the resource management and versioning systems employed + by various storage repositories. These repositories require control + over what sort of locking will be made available. For example, some + repositories only support shared write locks, while others only + provide support for exclusive write locks, while yet others use no + locking at all. As each system is sufficiently different to merit + exclusion of certain locking features, this specification leaves + locking as the sole axis of negotiation within WebDAV. + +6.4. Lock Creator and Privileges + + The creator of a lock has special privileges to use the lock to + modify the resource. When a locked resource is modified, a server + MUST check that the authenticated principal matches the lock creator + (in addition to checking for valid lock token submission). + + The server MAY allow privileged users other than the lock creator to + destroy a lock (for example, the resource owner or an administrator). + The 'unlock' privilege in [RFC3744] was defined to provide that + permission. + + There is no requirement for servers to accept LOCK requests from all + users or from anonymous users. + + Note that having a lock does not confer full privilege to modify the + locked resource. Write access and other privileges MUST be enforced + through normal privilege or authentication mechanisms, not based on + the possible obscurity of lock token values. + + + + + +Dusseault Standards Track [Page 20] + +RFC 4918 WebDAV June 2007 + + +6.5. Lock Tokens + + A lock token is a type of state token that identifies a particular + lock. Each lock has exactly one unique lock token generated by the + server. Clients MUST NOT attempt to interpret lock tokens in any + way. + + Lock token URIs MUST be unique across all resources for all time. + This uniqueness constraint allows lock tokens to be submitted across + resources and servers without fear of confusion. Since lock tokens + are unique, a client MAY submit a lock token in an If header on a + resource other than the one that returned it. + + When a LOCK operation creates a new lock, the new lock token is + returned in the Lock-Token response header defined in Section 10.5, + and also in the body of the response. + + Servers MAY make lock tokens publicly readable (e.g., in the DAV: + lockdiscovery property). One use case for making lock tokens + readable is so that a long-lived lock can be removed by the resource + owner (the client that obtained the lock might have crashed or + disconnected before cleaning up the lock). Except for the case of + using UNLOCK under user guidance, a client SHOULD NOT use a lock + token created by another client instance. + + This specification encourages servers to create Universally Unique + Identifiers (UUIDs) for lock tokens, and to use the URI form defined + by "A Universally Unique Identifier (UUID) URN Namespace" + ([RFC4122]). However, servers are free to use any URI (e.g., from + another scheme) so long as it meets the uniqueness requirements. For + example, a valid lock token might be constructed using the + "opaquelocktoken" scheme defined in Appendix C. + + Example: "urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6" + +6.6. Lock Timeout + + A lock MAY have a limited lifetime. The lifetime is suggested by the + client when creating or refreshing the lock, but the server + ultimately chooses the timeout value. Timeout is measured in seconds + remaining until lock expiration. + + The timeout counter MUST be restarted if a refresh lock request is + successful (see Section 9.10.2). The timeout counter SHOULD NOT be + restarted at any other time. + + If the timeout expires, then the lock SHOULD be removed. In this + case the server SHOULD act as if an UNLOCK method was executed by the + + + +Dusseault Standards Track [Page 21] + +RFC 4918 WebDAV June 2007 + + + server on the resource using the lock token of the timed-out lock, + performed with its override authority. + + Servers are advised to pay close attention to the values submitted by + clients, as they will be indicative of the type of activity the + client intends to perform. For example, an applet running in a + browser may need to lock a resource, but because of the instability + of the environment within which the applet is running, the applet may + be turned off without warning. As a result, the applet is likely to + ask for a relatively small timeout value so that if the applet dies, + the lock can be quickly harvested. However, a document management + system is likely to ask for an extremely long timeout because its + user may be planning on going offline. + + A client MUST NOT assume that just because the timeout has expired, + the lock has immediately been removed. + + Likewise, a client MUST NOT assume that just because the timeout has + not expired, the lock still exists. Clients MUST assume that locks + can arbitrarily disappear at any time, regardless of the value given + in the Timeout header. The Timeout header only indicates the + behavior of the server if extraordinary circumstances do not occur. + For example, a sufficiently privileged user may remove a lock at any + time, or the system may crash in such a way that it loses the record + of the lock's existence. + +6.7. Lock Capability Discovery + + Since server lock support is optional, a client trying to lock a + resource on a server can either try the lock and hope for the best, + or perform some form of discovery to determine what lock capabilities + the server supports. This is known as lock capability discovery. A + client can determine what lock types the server supports by + retrieving the DAV:supportedlock property. + + Any DAV-compliant resource that supports the LOCK method MUST support + the DAV:supportedlock property. + +6.8. Active Lock Discovery + + If another principal locks a resource that a principal wishes to + access, it is useful for the second principal to be able to find out + who the first principal is. For this purpose the DAV:lockdiscovery + property is provided. This property lists all outstanding locks, + describes their type, and MAY even provide the lock tokens. + + Any DAV-compliant resource that supports the LOCK method MUST support + the DAV:lockdiscovery property. + + + +Dusseault Standards Track [Page 22] + +RFC 4918 WebDAV June 2007 + + +7. Write Lock + + This section describes the semantics specific to the write lock type. + The write lock is a specific instance of a lock type, and is the only + lock type described in this specification. + + An exclusive write lock protects a resource: it prevents changes by + any principal other than the lock creator and in any case where the + lock token is not submitted (e.g., by a client process other than the + one holding the lock). + + Clients MUST submit a lock-token they are authorized to use in any + request that modifies a write-locked resource. The list of + modifications covered by a write-lock include: + + 1. A change to any of the following aspects of any write-locked + resource: + + * any variant, + + * any dead property, + + * any live property that is lockable (a live property is + lockable unless otherwise defined.) + + 2. For collections, any modification of an internal member URI. An + internal member URI of a collection is considered to be modified + if it is added, removed, or identifies a different resource. + More discussion on write locks and collections is found in + Section 7.4. + + 3. A modification of the mapping of the root of the write lock, + either to another resource or to no resource (e.g., DELETE). + + Of the methods defined in HTTP and WebDAV, PUT, POST, PROPPATCH, + LOCK, UNLOCK, MOVE, COPY (for the destination resource), DELETE, and + MKCOL are affected by write locks. All other HTTP/WebDAV methods + defined so far -- GET in particular -- function independently of a + write lock. + + The next few sections describe in more specific terms how write locks + interact with various operations. + + + + + + + + + +Dusseault Standards Track [Page 23] + +RFC 4918 WebDAV June 2007 + + +7.1. Write Locks and Properties + + While those without a write lock may not alter a property on a + resource it is still possible for the values of live properties to + change, even while locked, due to the requirements of their schemas. + Only dead properties and live properties defined as lockable are + guaranteed not to change while write locked. + +7.2. Avoiding Lost Updates + + Although the write locks provide some help in preventing lost + updates, they cannot guarantee that updates will never be lost. + Consider the following scenario: + + Two clients A and B are interested in editing the resource + 'index.html'. Client A is an HTTP client rather than a WebDAV + client, and so does not know how to perform locking. + + Client A doesn't lock the document, but does a GET, and begins + editing. + + Client B does LOCK, performs a GET and begins editing. + + Client B finishes editing, performs a PUT, then an UNLOCK. + + Client A performs a PUT, overwriting and losing all of B's changes. + + There are several reasons why the WebDAV protocol itself cannot + prevent this situation. First, it cannot force all clients to use + locking because it must be compatible with HTTP clients that do not + comprehend locking. Second, it cannot require servers to support + locking because of the variety of repository implementations, some of + which rely on reservations and merging rather than on locking. + Finally, being stateless, it cannot enforce a sequence of operations + like LOCK / GET / PUT / UNLOCK. + + WebDAV servers that support locking can reduce the likelihood that + clients will accidentally overwrite each other's changes by requiring + clients to lock resources before modifying them. Such servers would + effectively prevent HTTP 1.0 and HTTP 1.1 clients from modifying + resources. + + WebDAV clients can be good citizens by using a lock / retrieve / + write /unlock sequence of operations (at least by default) whenever + they interact with a WebDAV server that supports locking. + + + + + + +Dusseault Standards Track [Page 24] + +RFC 4918 WebDAV June 2007 + + + HTTP 1.1 clients can be good citizens, avoiding overwriting other + clients' changes, by using entity tags in If-Match headers with any + requests that would modify resources. + + Information managers may attempt to prevent overwrites by + implementing client-side procedures requiring locking before + modifying WebDAV resources. + +7.3. Write Locks and Unmapped URLs + + WebDAV provides the ability to send a LOCK request to an unmapped URL + in order to reserve the name for use. This is a simple way to avoid + the lost-update problem on the creation of a new resource (another + way is to use If-None-Match header specified in Section 14.26 of + [RFC2616]). It has the side benefit of locking the new resource + immediately for use of the creator. + + Note that the lost-update problem is not an issue for collections + because MKCOL can only be used to create a collection, not to + overwrite an existing collection. When trying to lock a collection + upon creation, clients can attempt to increase the likelihood of + getting the lock by pipelining the MKCOL and LOCK requests together + (but because this doesn't convert two separate operations into one + atomic operation, there's no guarantee this will work). + + A successful lock request to an unmapped URL MUST result in the + creation of a locked (non-collection) resource with empty content. + Subsequently, a successful PUT request (with the correct lock token) + provides the content for the resource. Note that the LOCK request + has no mechanism for the client to provide Content-Type or Content- + Language, thus the server will use defaults or empty values and rely + on the subsequent PUT request for correct values. + + A resource created with a LOCK is empty but otherwise behaves in + every way as a normal resource. It behaves the same way as a + resource created by a PUT request with an empty body (and where a + Content-Type and Content-Language was not specified), followed by a + LOCK request to the same resource. Following from this model, a + locked empty resource: + + o Can be read, deleted, moved, and copied, and in all ways behaves + as a regular non-collection resource. + + o Appears as a member of its parent collection. + + o SHOULD NOT disappear when its lock goes away (clients must + therefore be responsible for cleaning up their own mess, as with + any other operation or any non-empty resource). + + + +Dusseault Standards Track [Page 25] + +RFC 4918 WebDAV June 2007 + + + o MAY NOT have values for properties like DAV:getcontentlanguage + that haven't been specified yet by the client. + + o Can be updated (have content added) with a PUT request. + + o MUST NOT be converted into a collection. The server MUST fail a + MKCOL request (as it would with a MKCOL request to any existing + non-collection resource). + + o MUST have defined values for DAV:lockdiscovery and DAV: + supportedlock properties. + + o The response MUST indicate that a resource was created, by use of + the "201 Created" response code (a LOCK request to an existing + resource instead will result in 200 OK). The body must still + include the DAV:lockdiscovery property, as with a LOCK request to + an existing resource. + + The client is expected to update the locked empty resource shortly + after locking it, using PUT and possibly PROPPATCH. + + Alternatively and for backwards compatibility to [RFC2518], servers + MAY implement Lock-Null Resources (LNRs) instead (see definition in + Appendix D). Clients can easily interoperate both with servers that + support the old model LNRs and the recommended model of "locked empty + resources" by only attempting PUT after a LOCK to an unmapped URL, + not MKCOL or GET, and by not relying on specific properties of LNRs. + +7.4. Write Locks and Collections + + There are two kinds of collection write locks. A depth-0 write lock + on a collection protects the collection properties plus the internal + member URLs of that one collection, while not protecting the content + or properties of member resources (if the collection itself has any + entity bodies, those are also protected). A depth-infinity write + lock on a collection provides the same protection on that collection + and also provides write lock protection on every member resource. + + Expressed otherwise, a write lock of either kind protects any request + that would create a new resource in a write locked collection, any + request that would remove an internal member URL of a write locked + collection, and any request that would change the segment name of any + internal member. + + Thus, a collection write lock protects all the following actions: + + o DELETE a collection's direct internal member, + + + + +Dusseault Standards Track [Page 26] + +RFC 4918 WebDAV June 2007 + + + o MOVE an internal member out of the collection, + + o MOVE an internal member into the collection, + + o MOVE to rename an internal member within a collection, + + o COPY an internal member into a collection, and + + o PUT or MKCOL request that would create a new internal member. + + The collection's lock token is required in addition to the lock token + on the internal member itself, if it is locked separately. + + In addition, a depth-infinity lock affects all write operations to + all members of the locked collection. With a depth-infinity lock, + the resource identified by the root of the lock is directly locked, + and all its members are indirectly locked. + + o Any new resource added as a descendant of a depth-infinity locked + collection becomes indirectly locked. + + o Any indirectly locked resource moved out of the locked collection + into an unlocked collection is thereafter unlocked. + + o Any indirectly locked resource moved out of a locked source + collection into a depth-infinity locked target collection remains + indirectly locked but is now protected by the lock on the target + collection (the target collection's lock token will thereafter be + required to make further changes). + + If a depth-infinity write LOCK request is issued to a collection + containing member URLs identifying resources that are currently + locked in a manner that conflicts with the new lock (see Section 6.1, + point 3), the request MUST fail with a 423 (Locked) status code, and + the response SHOULD contain the 'no-conflicting-lock' precondition. + + If a lock request causes the URL of a resource to be added as an + internal member URL of a depth-infinity locked collection, then the + new resource MUST be automatically protected by the lock. For + example, if the collection /a/b/ is write locked and the resource /c + is moved to /a/b/c, then resource /a/b/c will be added to the write + lock. + + + + + + + + + +Dusseault Standards Track [Page 27] + +RFC 4918 WebDAV June 2007 + + +7.5. Write Locks and the If Request Header + + A user agent has to demonstrate knowledge of a lock when requesting + an operation on a locked resource. Otherwise, the following scenario + might occur. In the scenario, program A, run by User A, takes out a + write lock on a resource. Program B, also run by User A, has no + knowledge of the lock taken out by program A, yet performs a PUT to + the locked resource. In this scenario, the PUT succeeds because + locks are associated with a principal, not a program, and thus + program B, because it is acting with principal A's credential, is + allowed to perform the PUT. However, had program B known about the + lock, it would not have overwritten the resource, preferring instead + to present a dialog box describing the conflict to the user. Due to + this scenario, a mechanism is needed to prevent different programs + from accidentally ignoring locks taken out by other programs with the + same authorization. + + In order to prevent these collisions, a lock token MUST be submitted + by an authorized principal for all locked resources that a method may + change or the method MUST fail. A lock token is submitted when it + appears in an If header. For example, if a resource is to be moved + and both the source and destination are locked, then two lock tokens + must be submitted in the If header, one for the source and the other + for the destination. + +7.5.1. Example - Write Lock and COPY + + >>Request + + COPY /~fielding/index.html HTTP/1.1 + Host: www.example.com + Destination: http://www.example.com/users/f/fielding/index.html + If: + () + + >>Response + + HTTP/1.1 204 No Content + + In this example, even though both the source and destination are + locked, only one lock token must be submitted (the one for the lock + on the destination). This is because the source resource is not + modified by a COPY, and hence unaffected by the write lock. In this + example, user agent authentication has previously occurred via a + mechanism outside the scope of the HTTP protocol, in the underlying + transport layer. + + + + + +Dusseault Standards Track [Page 28] + +RFC 4918 WebDAV June 2007 + + +7.5.2. Example - Deleting a Member of a Locked Collection + + Consider a collection "/locked" with an exclusive, depth-infinity + write lock, and an attempt to delete an internal member "/locked/ + member": + + >>Request + + DELETE /locked/member HTTP/1.1 + Host: example.com + + >>Response + + HTTP/1.1 423 Locked + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + /locked/ + + + + Thus, the client would need to submit the lock token with the request + to make it succeed. To do that, various forms of the If header (see + Section 10.4) could be used. + + "No-Tag-List" format: + + If: () + + "Tagged-List" format, for "http://example.com/locked/": + + If: + () + + "Tagged-List" format, for "http://example.com/locked/member": + + If: + () + + Note that, for the purpose of submitting the lock token, the actual + form doesn't matter; what's relevant is that the lock token appears + in the If header, and that the If header itself evaluates to true. + + + + + + +Dusseault Standards Track [Page 29] + +RFC 4918 WebDAV June 2007 + + +7.6. Write Locks and COPY/MOVE + + A COPY method invocation MUST NOT duplicate any write locks active on + the source. However, as previously noted, if the COPY copies the + resource into a collection that is locked with a depth-infinity lock, + then the resource will be added to the lock. + + A successful MOVE request on a write locked resource MUST NOT move + the write lock with the resource. However, if there is an existing + lock at the destination, the server MUST add the moved resource to + the destination lock scope. For example, if the MOVE makes the + resource a child of a collection that has a depth-infinity lock, then + the resource will be added to that collection's lock. Additionally, + if a resource with a depth-infinity lock is moved to a destination + that is within the scope of the same lock (e.g., within the URL + namespace tree covered by the lock), the moved resource will again be + added to the lock. In both these examples, as specified in + Section 7.5, an If header must be submitted containing a lock token + for both the source and destination. + +7.7. Refreshing Write Locks + + A client MUST NOT submit the same write lock request twice. Note + that a client is always aware it is resubmitting the same lock + request because it must include the lock token in the If header in + order to make the request for a resource that is already locked. + + However, a client may submit a LOCK request with an If header but + without a body. A server receiving a LOCK request with no body MUST + NOT create a new lock -- this form of the LOCK request is only to be + used to "refresh" an existing lock (meaning, at minimum, that any + timers associated with the lock MUST be reset). + + Clients may submit Timeout headers of arbitrary value with their lock + refresh requests. Servers, as always, may ignore Timeout headers + submitted by the client, and a server MAY refresh a lock with a + timeout period that is different than the previous timeout period + used for the lock, provided it advertises the new value in the LOCK + refresh response. + + If an error is received in response to a refresh LOCK request, the + client MUST NOT assume that the lock was refreshed. + + + + + + + + + +Dusseault Standards Track [Page 30] + +RFC 4918 WebDAV June 2007 + + +8. General Request and Response Handling + +8.1. Precedence in Error Handling + + Servers MUST return authorization errors in preference to other + errors. This avoids leaking information about protected resources + (e.g., a client that finds that a hidden resource exists by seeing a + 423 Locked response to an anonymous request to the resource). + +8.2. Use of XML + + In HTTP/1.1, method parameter information was exclusively encoded in + HTTP headers. Unlike HTTP/1.1, WebDAV encodes method parameter + information either in an XML ([REC-XML]) request entity body, or in + an HTTP header. The use of XML to encode method parameters was + motivated by the ability to add extra XML elements to existing + structures, providing extensibility; and by XML's ability to encode + information in ISO 10646 character sets, providing + internationalization support. + + In addition to encoding method parameters, XML is used in WebDAV to + encode the responses from methods, providing the extensibility and + internationalization advantages of XML for method output, as well as + input. + + When XML is used for a request or response body, the Content-Type + type SHOULD be application/xml. Implementations MUST accept both + text/xml and application/xml in request and response bodies. Use of + text/xml is deprecated. + + All DAV-compliant clients and resources MUST use XML parsers that are + compliant with [REC-XML] and [REC-XML-NAMES]. All XML used in either + requests or responses MUST be, at minimum, well formed and use + namespaces correctly. If a server receives XML that is not well- + formed, then the server MUST reject the entire request with a 400 + (Bad Request). If a client receives XML that is not well-formed in a + response, then the client MUST NOT assume anything about the outcome + of the executed method and SHOULD treat the server as malfunctioning. + + Note that processing XML submitted by an untrusted source may cause + risks connected to privacy, security, and service quality (see + Section 20). Servers MAY reject questionable requests (even though + they consist of well-formed XML), for instance, with a 400 (Bad + Request) status code and an optional response body explaining the + problem. + + + + + + +Dusseault Standards Track [Page 31] + +RFC 4918 WebDAV June 2007 + + +8.3. URL Handling + + URLs appear in many places in requests and responses. + Interoperability experience with [RFC2518] showed that many clients + parsing Multi-Status responses did not fully implement the full + Reference Resolution defined in Section 5 of [RFC3986]. Thus, + servers in particular need to be careful in handling URLs in + responses, to ensure that clients have enough context to be able to + interpret all the URLs. The rules in this section apply not only to + resource URLs in the 'href' element in Multi-Status responses, but + also to the Destination and If header resource URLs. + + The sender has a choice between two approaches: using a relative + reference, which is resolved against the Request-URI, or a full URI. + A server MUST ensure that every 'href' value within a Multi-Status + response uses the same format. + + WebDAV only uses one form of relative reference in its extensions, + the absolute path. + + Simple-ref = absolute-URI | ( path-absolute [ "?" query ] ) + + The absolute-URI, path-absolute and query productions are defined in + Sections 4.3, 3.3, and 3.4 of [RFC3986]. + + Within Simple-ref productions, senders MUST NOT: + + o use dot-segments ("." or ".."), or + + o have prefixes that do not match the Request-URI (using the + comparison rules defined in Section 3.2.3 of [RFC2616]). + + Identifiers for collections SHOULD end in a '/' character. + +8.3.1. Example - Correct URL Handling + + Consider the collection http://example.com/sample/ with the internal + member URL http://example.com/sample/a%20test and the PROPFIND + request below: + + >>Request: + + PROPFIND /sample/ HTTP/1.1 + Host: example.com + Depth: 1 + + + + + + +Dusseault Standards Track [Page 32] + +RFC 4918 WebDAV June 2007 + + + In this case, the server should return two 'href' elements containing + either + + o 'http://example.com/sample/' and + 'http://example.com/sample/a%20test', or + + o '/sample/' and '/sample/a%20test' + + Note that even though the server may be storing the member resource + internally as 'a test', it has to be percent-encoded when used inside + a URI reference (see Section 2.1 of [RFC3986]). Also note that a + legal URI may still contain characters that need to be escaped within + XML character data, such as the ampersand character. + +8.4. Required Bodies in Requests + + Some of these new methods do not define bodies. Servers MUST examine + all requests for a body, even when a body was not expected. In cases + where a request body is present but would be ignored by a server, the + server MUST reject the request with 415 (Unsupported Media Type). + This informs the client (which may have been attempting to use an + extension) that the body could not be processed as the client + intended. + +8.5. HTTP Headers for Use in WebDAV + + HTTP defines many headers that can be used in WebDAV requests and + responses. Not all of these are appropriate in all situations and + some interactions may be undefined. Note that HTTP 1.1 requires the + Date header in all responses if possible (see Section 14.18, + [RFC2616]). + + The server MUST do authorization checks before checking any HTTP + conditional header. + +8.6. ETag + + HTTP 1.1 recommends the use of ETags rather than modification dates, + for cache control, and there are even stronger reasons to prefer + ETags for authoring. Correct use of ETags is even more important in + a distributed authoring environment, because ETags are necessary + along with locks to avoid the lost-update problem. A client might + fail to renew a lock, for example, when the lock times out and the + client is accidentally offline or in the middle of a long upload. + When a client fails to renew the lock, it's quite possible the + resource can still be relocked and the user can go on editing, as + long as no changes were made in the meantime. ETags are required for + the client to be able to distinguish this case. Otherwise, the + + + +Dusseault Standards Track [Page 33] + +RFC 4918 WebDAV June 2007 + + + client is forced to ask the user whether to overwrite the resource on + the server without even being able to tell the user if it has + changed. Timestamps do not solve this problem nearly as well as + ETags. + + Strong ETags are much more useful for authoring use cases than weak + ETags (see Section 13.3.3 of [RFC2616]). Semantic equivalence can be + a useful concept but that depends on the document type and the + application type, and interoperability might require some agreement + or standard outside the scope of this specification and HTTP. Note + also that weak ETags have certain restrictions in HTTP, e.g., these + cannot be used in If-Match headers. + + Note that the meaning of an ETag in a PUT response is not clearly + defined either in this document or in RFC 2616 (i.e., whether the + ETag means that the resource is octet-for-octet equivalent to the + body of the PUT request, or whether the server could have made minor + changes in the formatting or content of the document upon storage). + This is an HTTP issue, not purely a WebDAV issue. + + Because clients may be forced to prompt users or throw away changed + content if the ETag changes, a WebDAV server SHOULD NOT change the + ETag (or the Last-Modified time) for a resource that has an unchanged + body and location. The ETag represents the state of the body or + contents of the resource. There is no similar way to tell if + properties have changed. + +8.7. Including Error Response Bodies + + HTTP and WebDAV did not use the bodies of most error responses for + machine-parsable information until the specification for Versioning + Extensions to WebDAV introduced a mechanism to include more specific + information in the body of an error response (Section 1.6 of + [RFC3253]). The error body mechanism is appropriate to use with any + error response that may take a body but does not already have a body + defined. The mechanism is particularly appropriate when a status + code can mean many things (for example, 400 Bad Request can mean + required headers are missing, headers are incorrectly formatted, or + much more). This error body mechanism is covered in Section 16. + +8.8. Impact of Namespace Operations on Cache Validators + + Note that the HTTP response headers "Etag" and "Last-Modified" (see + [RFC2616], Sections 14.19 and 14.29) are defined per URL (not per + resource), and are used by clients for caching. Therefore servers + must ensure that executing any operation that affects the URL + namespace (such as COPY, MOVE, DELETE, PUT, or MKCOL) does preserve + their semantics, in particular: + + + +Dusseault Standards Track [Page 34] + +RFC 4918 WebDAV June 2007 + + + o For any given URL, the "Last-Modified" value MUST increment every + time the representation returned upon GET changes (within the + limits of timestamp resolution). + + o For any given URL, an "ETag" value MUST NOT be reused for + different representations returned by GET. + + In practice this means that servers + + o might have to increment "Last-Modified" timestamps for every + resource inside the destination namespace of a namespace operation + unless it can do so more selectively, and + + o similarly, might have to re-assign "ETag" values for these + resources (unless the server allocates entity tags in a way so + that they are unique across the whole URL namespace managed by the + server). + + Note that these considerations also apply to specific use cases, such + as using PUT to create a new resource at a URL that has been mapped + before, but has been deleted since then. + + Finally, WebDAV properties (such as DAV:getetag and DAV: + getlastmodified) that inherit their semantics from HTTP headers must + behave accordingly. + +9. HTTP Methods for Distributed Authoring + +9.1. PROPFIND Method + + The PROPFIND method retrieves properties defined on the resource + identified by the Request-URI, if the resource does not have any + internal members, or on the resource identified by the Request-URI + and potentially its member resources, if the resource is a collection + that has internal member URLs. All DAV-compliant resources MUST + support the PROPFIND method and the propfind XML element + (Section 14.20) along with all XML elements defined for use with that + element. + + A client MUST submit a Depth header with a value of "0", "1", or + "infinity" with a PROPFIND request. Servers MUST support "0" and "1" + depth requests on WebDAV-compliant resources and SHOULD support + "infinity" requests. In practice, support for infinite-depth + requests MAY be disabled, due to the performance and security + concerns associated with this behavior. Servers SHOULD treat a + request without a Depth header as if a "Depth: infinity" header was + included. + + + + +Dusseault Standards Track [Page 35] + +RFC 4918 WebDAV June 2007 + + + A client may submit a 'propfind' XML element in the body of the + request method describing what information is being requested. It is + possible to: + + o Request particular property values, by naming the properties + desired within the 'prop' element (the ordering of properties in + here MAY be ignored by the server), + + o Request property values for those properties defined in this + specification (at a minimum) plus dead properties, by using the + 'allprop' element (the 'include' element can be used with + 'allprop' to instruct the server to also include additional live + properties that may not have been returned otherwise), + + o Request a list of names of all the properties defined on the + resource, by using the 'propname' element. + + A client may choose not to submit a request body. An empty PROPFIND + request body MUST be treated as if it were an 'allprop' request. + + Note that 'allprop' does not return values for all live properties. + WebDAV servers increasingly have expensively-calculated or lengthy + properties (see [RFC3253] and [RFC3744]) and do not return all + properties already. Instead, WebDAV clients can use propname + requests to discover what live properties exist, and request named + properties when retrieving values. For a live property defined + elsewhere, that definition can specify whether or not that live + property would be returned in 'allprop' requests. + + All servers MUST support returning a response of content type text/ + xml or application/xml that contains a multistatus XML element that + describes the results of the attempts to retrieve the various + properties. + + If there is an error retrieving a property, then a proper error + result MUST be included in the response. A request to retrieve the + value of a property that does not exist is an error and MUST be noted + with a 'response' XML element that contains a 404 (Not Found) status + value. + + Consequently, the 'multistatus' XML element for a collection resource + MUST include a 'response' XML element for each member URL of the + collection, to whatever depth was requested. It SHOULD NOT include + any 'response' elements for resources that are not WebDAV-compliant. + Each 'response' element MUST contain an 'href' element that contains + the URL of the resource on which the properties in the prop XML + element are defined. Results for a PROPFIND on a collection resource + are returned as a flat list whose order of entries is not + + + +Dusseault Standards Track [Page 36] + +RFC 4918 WebDAV June 2007 + + + significant. Note that a resource may have only one value for a + property of a given name, so the property may only show up once in + PROPFIND responses. + + Properties may be subject to access control. In the case of + 'allprop' and 'propname' requests, if a principal does not have the + right to know whether a particular property exists, then the property + MAY be silently excluded from the response. + + Some PROPFIND results MAY be cached, with care, as there is no cache + validation mechanism for most properties. This method is both safe + and idempotent (see Section 9.1 of [RFC2616]). + +9.1.1. PROPFIND Status Codes + + This section, as with similar sections for other methods, provides + some guidance on error codes and preconditions or postconditions + (defined in Section 16) that might be particularly useful with + PROPFIND. + + 403 Forbidden - A server MAY reject PROPFIND requests on collections + with depth header of "Infinity", in which case it SHOULD use this + error with the precondition code 'propfind-finite-depth' inside the + error body. + +9.1.2. Status Codes for Use in 'propstat' Element + + In PROPFIND responses, information about individual properties is + returned inside 'propstat' elements (see Section 14.22), each + containing an individual 'status' element containing information + about the properties appearing in it. The list below summarizes the + most common status codes used inside 'propstat'; however, clients + should be prepared to handle other 2/3/4/5xx series status codes as + well. + + 200 OK - A property exists and/or its value is successfully returned. + + 401 Unauthorized - The property cannot be viewed without appropriate + authorization. + + 403 Forbidden - The property cannot be viewed regardless of + authentication. + + 404 Not Found - The property does not exist. + + + + + + + +Dusseault Standards Track [Page 37] + +RFC 4918 WebDAV June 2007 + + +9.1.3. Example - Retrieving Named Properties + + >>Request + + PROPFIND /file HTTP/1.1 + Host: www.example.com + Content-type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.example.com/file + + + + Box type A + + + J.J. Johnson + + + HTTP/1.1 200 OK + + + + HTTP/1.1 403 Forbidden + The user does not have access to the + DingALing property. + + + + + +Dusseault Standards Track [Page 38] + +RFC 4918 WebDAV June 2007 + + + + There has been an access violation error. + + + + + In this example, PROPFIND is executed on a non-collection resource + http://www.example.com/file. The propfind XML element specifies the + name of four properties whose values are being requested. In this + case, only two properties were returned, since the principal issuing + the request did not have sufficient access rights to see the third + and fourth properties. + +9.1.4. Example - Using 'propname' to Retrieve All Property Names + + >>Request + + PROPFIND /container/ HTTP/1.1 + Host: www.example.com + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.example.com/container/ + + + + + + + + + + HTTP/1.1 200 OK + + + +Dusseault Standards Track [Page 39] + +RFC 4918 WebDAV June 2007 + + + + + + http://www.example.com/container/front.html + + + + + + + + + + + + + HTTP/1.1 200 OK + + + + + In this example, PROPFIND is invoked on the collection resource + http://www.example.com/container/, with a propfind XML element + containing the propname XML element, meaning the name of all + properties should be returned. Since no Depth header is present, it + assumes its default value of "infinity", meaning the name of the + properties on the collection and all its descendants should be + returned. + + Consistent with the previous example, resource + http://www.example.com/container/ has six properties defined on it: + bigbox and author in the "http://ns.example.com/boxschema/" + namespace, and creationdate, displayname, resourcetype, and + supportedlock in the "DAV:" namespace. + + The resource http://www.example.com/container/index.html, a member of + the "container" collection, has nine properties defined on it, bigbox + in the "http://ns.example.com/boxschema/" namespace and creationdate, + displayname, getcontentlength, getcontenttype, getetag, + getlastmodified, resourcetype, and supportedlock in the "DAV:" + namespace. + + This example also demonstrates the use of XML namespace scoping and + the default namespace. Since the "xmlns" attribute does not contain + a prefix, the namespace applies by default to all enclosed elements. + Hence, all elements that do not explicitly state the namespace to + which they belong are members of the "DAV:" namespace. + + + + +Dusseault Standards Track [Page 40] + +RFC 4918 WebDAV June 2007 + + +9.1.5. Example - Using So-called 'allprop' + + Note that 'allprop', despite its name, which remains for backward- + compatibility, does not return every property, but only dead + properties and the live properties defined in this specification. + + >>Request + + PROPFIND /container/ HTTP/1.1 + Host: www.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + /container/ + + + Box type A + Hadrian + 1997-12-01T17:42:21-08:00 + Example collection + + + + + + + + + + + + + + + +Dusseault Standards Track [Page 41] + +RFC 4918 WebDAV June 2007 + + + HTTP/1.1 200 OK + + + + /container/front.html + + + Box type B + + 1997-12-01T18:27:21-08:00 + Example HTML resource + 4525 + text/html + "zzyzx" + Mon, 12 Jan 1998 09:25:56 GMT + + + + + + + + + + + + + HTTP/1.1 200 OK + + + + + In this example, PROPFIND was invoked on the resource + http://www.example.com/container/ with a Depth header of 1, meaning + the request applies to the resource and its children, and a propfind + XML element containing the allprop XML element, meaning the request + should return the name and value of all the dead properties defined + on the resources, plus the name and value of all the properties + defined in this specification. This example illustrates the use of + relative references in the 'href' elements of the response. + + The resource http://www.example.com/container/ has six properties + defined on it: 'bigbox' and 'author in the + "http://ns.example.com/boxschema/" namespace, DAV:creationdate, DAV: + displayname, DAV:resourcetype, and DAV:supportedlock. + + + + + +Dusseault Standards Track [Page 42] + +RFC 4918 WebDAV June 2007 + + + The last four properties are WebDAV-specific, defined in Section 15. + Since GET is not supported on this resource, the get* properties + (e.g., DAV:getcontentlength) are not defined on this resource. The + WebDAV-specific properties assert that "container" was created on + December 1, 1997, at 5:42:21PM, in a time zone 8 hours west of GMT + (DAV:creationdate), has a name of "Example collection" (DAV: + displayname), a collection resource type (DAV:resourcetype), and + supports exclusive write and shared write locks (DAV:supportedlock). + + The resource http://www.example.com/container/front.html has nine + properties defined on it: + + 'bigbox' in the "http://ns.example.com/boxschema/" namespace (another + instance of the "bigbox" property type), DAV:creationdate, DAV: + displayname, DAV:getcontentlength, DAV:getcontenttype, DAV:getetag, + DAV:getlastmodified, DAV:resourcetype, and DAV:supportedlock. + + The DAV-specific properties assert that "front.html" was created on + December 1, 1997, at 6:27:21PM, in a time zone 8 hours west of GMT + (DAV:creationdate), has a name of "Example HTML resource" (DAV: + displayname), a content length of 4525 bytes (DAV:getcontentlength), + a MIME type of "text/html" (DAV:getcontenttype), an entity tag of + "zzyzx" (DAV:getetag), was last modified on Monday, January 12, 1998, + at 09:25:56 GMT (DAV:getlastmodified), has an empty resource type, + meaning that it is not a collection (DAV:resourcetype), and supports + both exclusive write and shared write locks (DAV:supportedlock). + +9.1.6. Example - Using 'allprop' with 'include' + + >>Request + + PROPFIND /mycol/ HTTP/1.1 + Host: www.example.com + Depth: 1 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + +Dusseault Standards Track [Page 43] + +RFC 4918 WebDAV June 2007 + + + In this example, PROPFIND is executed on the resource + http://www.example.com/mycol/ and its internal member resources. The + client requests the values of all live properties defined in this + specification, plus all dead properties, plus two more live + properties defined in [RFC3253]. The response is not shown. + +9.2. PROPPATCH Method + + The PROPPATCH method processes instructions specified in the request + body to set and/or remove properties defined on the resource + identified by the Request-URI. + + All DAV-compliant resources MUST support the PROPPATCH method and + MUST process instructions that are specified using the + propertyupdate, set, and remove XML elements. Execution of the + directives in this method is, of course, subject to access control + constraints. DAV-compliant resources SHOULD support the setting of + arbitrary dead properties. + + The request message body of a PROPPATCH method MUST contain the + propertyupdate XML element. + + Servers MUST process PROPPATCH instructions in document order (an + exception to the normal rule that ordering is irrelevant). + Instructions MUST either all be executed or none executed. Thus, if + any error occurs during processing, all executed instructions MUST be + undone and a proper error result returned. Instruction processing + details can be found in the definition of the set and remove + instructions in Sections 14.23 and 14.26. + + If a server attempts to make any of the property changes in a + PROPPATCH request (i.e., the request is not rejected for high-level + errors before processing the body), the response MUST be a Multi- + Status response as described in Section 9.2.1. + + This method is idempotent, but not safe (see Section 9.1 of + [RFC2616]). Responses to this method MUST NOT be cached. + +9.2.1. Status Codes for Use in 'propstat' Element + + In PROPPATCH responses, information about individual properties is + returned inside 'propstat' elements (see Section 14.22), each + containing an individual 'status' element containing information + about the properties appearing in it. The list below summarizes the + most common status codes used inside 'propstat'; however, clients + should be prepared to handle other 2/3/4/5xx series status codes as + well. + + + + +Dusseault Standards Track [Page 44] + +RFC 4918 WebDAV June 2007 + + + 200 (OK) - The property set or change succeeded. Note that if this + appears for one property, it appears for every property in the + response, due to the atomicity of PROPPATCH. + + 403 (Forbidden) - The client, for reasons the server chooses not to + specify, cannot alter one of the properties. + + 403 (Forbidden): The client has attempted to set a protected + property, such as DAV:getetag. If returning this error, the server + SHOULD use the precondition code 'cannot-modify-protected-property' + inside the response body. + + 409 (Conflict) - The client has provided a value whose semantics are + not appropriate for the property. + + 424 (Failed Dependency) - The property change could not be made + because of another property change that failed. + + 507 (Insufficient Storage) - The server did not have sufficient space + to record the property. + +9.2.2. Example - PROPPATCH + + >>Request + + PROPPATCH /bar.html HTTP/1.1 + Host: www.example.com + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + Jim Whitehead + Roy Fielding + + + + + + + + + + + + + +Dusseault Standards Track [Page 45] + +RFC 4918 WebDAV June 2007 + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.example.com/bar.html + + + HTTP/1.1 424 Failed Dependency + + + + HTTP/1.1 409 Conflict + + Copyright Owner cannot be deleted or + altered. + + + + In this example, the client requests the server to set the value of + the "Authors" property in the + "http://ns.example.com/standards/z39.50/" namespace, and to remove + the property "Copyright-Owner" in the same namespace. Since the + Copyright-Owner property could not be removed, no property + modifications occur. The 424 (Failed Dependency) status code for the + Authors property indicates this action would have succeeded if it + were not for the conflict with removing the Copyright-Owner property. + +9.3. MKCOL Method + + MKCOL creates a new collection resource at the location specified by + the Request-URI. If the Request-URI is already mapped to a resource, + then the MKCOL MUST fail. During MKCOL processing, a server MUST + make the Request-URI an internal member of its parent collection, + unless the Request-URI is "/". If no such ancestor exists, the + method MUST fail. When the MKCOL operation creates a new collection + resource, all ancestors MUST already exist, or the method MUST fail + with a 409 (Conflict) status code. For example, if a request to + create collection /a/b/c/d/ is made, and /a/b/c/ does not exist, the + request must fail. + + When MKCOL is invoked without a request body, the newly created + collection SHOULD have no members. + + + +Dusseault Standards Track [Page 46] + +RFC 4918 WebDAV June 2007 + + + A MKCOL request message may contain a message body. The precise + behavior of a MKCOL request when the body is present is undefined, + but limited to creating collections, members of a collection, bodies + of members, and properties on the collections or members. If the + server receives a MKCOL request entity type it does not support or + understand, it MUST respond with a 415 (Unsupported Media Type) + status code. If the server decides to reject the request based on + the presence of an entity or the type of an entity, it should use the + 415 (Unsupported Media Type) status code. + + This method is idempotent, but not safe (see Section 9.1 of + [RFC2616]). Responses to this method MUST NOT be cached. + +9.3.1. MKCOL Status Codes + + In addition to the general status codes possible, the following + status codes have specific applicability to MKCOL: + + 201 (Created) - The collection was created. + + 403 (Forbidden) - This indicates at least one of two conditions: 1) + the server does not allow the creation of collections at the given + location in its URL namespace, or 2) the parent collection of the + Request-URI exists but cannot accept members. + + 405 (Method Not Allowed) - MKCOL can only be executed on an unmapped + URL. + + 409 (Conflict) - A collection cannot be made at the Request-URI until + one or more intermediate collections have been created. The server + MUST NOT create those intermediate collections automatically. + + 415 (Unsupported Media Type) - The server does not support the + request body type (although bodies are legal on MKCOL requests, since + this specification doesn't define any, the server is likely not to + support any given body type). + + 507 (Insufficient Storage) - The resource does not have sufficient + space to record the state of the resource after the execution of this + method. + +9.3.2. Example - MKCOL + + This example creates a collection called /webdisc/xfiles/ on the + server www.example.com. + + + + + + +Dusseault Standards Track [Page 47] + +RFC 4918 WebDAV June 2007 + + + >>Request + + MKCOL /webdisc/xfiles/ HTTP/1.1 + Host: www.example.com + + + >>Response + + HTTP/1.1 201 Created + +9.4. GET, HEAD for Collections + + The semantics of GET are unchanged when applied to a collection, + since GET is defined as, "retrieve whatever information (in the form + of an entity) is identified by the Request-URI" [RFC2616]. GET, when + applied to a collection, may return the contents of an "index.html" + resource, a human-readable view of the contents of the collection, or + something else altogether. Hence, it is possible that the result of + a GET on a collection will bear no correlation to the membership of + the collection. + + Similarly, since the definition of HEAD is a GET without a response + message body, the semantics of HEAD are unmodified when applied to + collection resources. + +9.5. POST for Collections + + Since by definition the actual function performed by POST is + determined by the server and often depends on the particular + resource, the behavior of POST when applied to collections cannot be + meaningfully modified because it is largely undefined. Thus, the + semantics of POST are unmodified when applied to a collection. + +9.6. DELETE Requirements + + DELETE is defined in [RFC2616], Section 9.7, to "delete the resource + identified by the Request-URI". However, WebDAV changes some DELETE + handling requirements. + + A server processing a successful DELETE request: + + MUST destroy locks rooted on the deleted resource + + MUST remove the mapping from the Request-URI to any resource. + + Thus, after a successful DELETE operation (and in the absence of + other actions), a subsequent GET/HEAD/PROPFIND request to the target + Request-URI MUST return 404 (Not Found). + + + +Dusseault Standards Track [Page 48] + +RFC 4918 WebDAV June 2007 + + +9.6.1. DELETE for Collections + + The DELETE method on a collection MUST act as if a "Depth: infinity" + header was used on it. A client MUST NOT submit a Depth header with + a DELETE on a collection with any value but infinity. + + DELETE instructs that the collection specified in the Request-URI and + all resources identified by its internal member URLs are to be + deleted. + + If any resource identified by a member URL cannot be deleted, then + all of the member's ancestors MUST NOT be deleted, so as to maintain + URL namespace consistency. + + Any headers included with DELETE MUST be applied in processing every + resource to be deleted. + + When the DELETE method has completed processing, it MUST result in a + consistent URL namespace. + + If an error occurs deleting a member resource (a resource other than + the resource identified in the Request-URI), then the response can be + a 207 (Multi-Status). Multi-Status is used here to indicate which + internal resources could NOT be deleted, including an error code, + which should help the client understand which resources caused the + failure. For example, the Multi-Status body could include a response + with status 423 (Locked) if an internal resource was locked. + + The server MAY return a 4xx status response, rather than a 207, if + the request failed completely. + + 424 (Failed Dependency) status codes SHOULD NOT be in the 207 (Multi- + Status) response for DELETE. They can be safely left out because the + client will know that the ancestors of a resource could not be + deleted when the client receives an error for the ancestor's progeny. + Additionally, 204 (No Content) errors SHOULD NOT be returned in the + 207 (Multi-Status). The reason for this prohibition is that 204 (No + Content) is the default success code. + +9.6.2. Example - DELETE + + >>Request + + DELETE /container/ HTTP/1.1 + Host: www.example.com + + + + + + +Dusseault Standards Track [Page 49] + +RFC 4918 WebDAV June 2007 + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.example.com/container/resource3 + HTTP/1.1 423 Locked + + + + + In this example, the attempt to delete + http://www.example.com/container/resource3 failed because it is + locked, and no lock token was submitted with the request. + Consequently, the attempt to delete http://www.example.com/container/ + also failed. Thus, the client knows that the attempt to delete + http://www.example.com/container/ must have also failed since the + parent cannot be deleted unless its child has also been deleted. + Even though a Depth header has not been included, a depth of infinity + is assumed because the method is on a collection. + +9.7. PUT Requirements + +9.7.1. PUT for Non-Collection Resources + + A PUT performed on an existing resource replaces the GET response + entity of the resource. Properties defined on the resource may be + recomputed during PUT processing but are not otherwise affected. For + example, if a server recognizes the content type of the request body, + it may be able to automatically extract information that could be + profitably exposed as properties. + + A PUT that would result in the creation of a resource without an + appropriately scoped parent collection MUST fail with a 409 + (Conflict). + + A PUT request allows a client to indicate what media type an entity + body has, and whether it should change if overwritten. Thus, a + client SHOULD provide a Content-Type for a new resource if any is + known. If the client does not provide a Content-Type for a new + resource, the server MAY create a resource with no Content-Type + assigned, or it MAY attempt to assign a Content-Type. + + + + + +Dusseault Standards Track [Page 50] + +RFC 4918 WebDAV June 2007 + + + Note that although a recipient ought generally to treat metadata + supplied with an HTTP request as authoritative, in practice there's + no guarantee that a server will accept client-supplied metadata + (e.g., any request header beginning with "Content-"). Many servers + do not allow configuring the Content-Type on a per-resource basis in + the first place. Thus, clients can't always rely on the ability to + directly influence the content type by including a Content-Type + request header. + +9.7.2. PUT for Collections + + This specification does not define the behavior of the PUT method for + existing collections. A PUT request to an existing collection MAY be + treated as an error (405 Method Not Allowed). + + The MKCOL method is defined to create collections. + +9.8. COPY Method + + The COPY method creates a duplicate of the source resource identified + by the Request-URI, in the destination resource identified by the URI + in the Destination header. The Destination header MUST be present. + The exact behavior of the COPY method depends on the type of the + source resource. + + All WebDAV-compliant resources MUST support the COPY method. + However, support for the COPY method does not guarantee the ability + to copy a resource. For example, separate programs may control + resources on the same server. As a result, it may not be possible to + copy a resource to a location that appears to be on the same server. + + This method is idempotent, but not safe (see Section 9.1 of + [RFC2616]). Responses to this method MUST NOT be cached. + +9.8.1. COPY for Non-collection Resources + + When the source resource is not a collection, the result of the COPY + method is the creation of a new resource at the destination whose + state and behavior match that of the source resource as closely as + possible. Since the environment at the destination may be different + than at the source due to factors outside the scope of control of the + server, such as the absence of resources required for correct + operation, it may not be possible to completely duplicate the + behavior of the resource at the destination. Subsequent alterations + to the destination resource will not modify the source resource. + Subsequent alterations to the source resource will not modify the + destination resource. + + + + +Dusseault Standards Track [Page 51] + +RFC 4918 WebDAV June 2007 + + +9.8.2. COPY for Properties + + After a successful COPY invocation, all dead properties on the source + resource SHOULD be duplicated on the destination resource. Live + properties described in this document SHOULD be duplicated as + identically behaving live properties at the destination resource, but + not necessarily with the same values. Servers SHOULD NOT convert + live properties into dead properties on the destination resource, + because clients may then draw incorrect conclusions about the state + or functionality of a resource. Note that some live properties are + defined such that the absence of the property has a specific meaning + (e.g., a flag with one meaning if present, and the opposite if + absent), and in these cases, a successful COPY might result in the + property being reported as "Not Found" in subsequent requests. + + When the destination is an unmapped URL, a COPY operation creates a + new resource much like a PUT operation does. Live properties that + are related to resource creation (such as DAV:creationdate) should + have their values set accordingly. + +9.8.3. COPY for Collections + + The COPY method on a collection without a Depth header MUST act as if + a Depth header with value "infinity" was included. A client may + submit a Depth header on a COPY on a collection with a value of "0" + or "infinity". Servers MUST support the "0" and "infinity" Depth + header behaviors on WebDAV-compliant resources. + + An infinite-depth COPY instructs that the collection resource + identified by the Request-URI is to be copied to the location + identified by the URI in the Destination header, and all its internal + member resources are to be copied to a location relative to it, + recursively through all levels of the collection hierarchy. Note + that an infinite-depth COPY of /A/ into /A/B/ could lead to infinite + recursion if not handled correctly. + + A COPY of "Depth: 0" only instructs that the collection and its + properties, but not resources identified by its internal member URLs, + are to be copied. + + Any headers included with a COPY MUST be applied in processing every + resource to be copied with the exception of the Destination header. + + The Destination header only specifies the destination URI for the + Request-URI. When applied to members of the collection identified by + the Request-URI, the value of Destination is to be modified to + reflect the current location in the hierarchy. So, if the Request- + URI is /a/ with Host header value http://example.com/ and the + + + +Dusseault Standards Track [Page 52] + +RFC 4918 WebDAV June 2007 + + + Destination is http://example.com/b/, then when + http://example.com/a/c/d is processed, it must use a Destination of + http://example.com/b/c/d. + + When the COPY method has completed processing, it MUST have created a + consistent URL namespace at the destination (see Section 5.1 for the + definition of namespace consistency). However, if an error occurs + while copying an internal collection, the server MUST NOT copy any + resources identified by members of this collection (i.e., the server + must skip this subtree), as this would create an inconsistent + namespace. After detecting an error, the COPY operation SHOULD try + to finish as much of the original copy operation as possible (i.e., + the server should still attempt to copy other subtrees and their + members that are not descendants of an error-causing collection). + + So, for example, if an infinite-depth copy operation is performed on + collection /a/, which contains collections /a/b/ and /a/c/, and an + error occurs copying /a/b/, an attempt should still be made to copy + /a/c/. Similarly, after encountering an error copying a non- + collection resource as part of an infinite-depth copy, the server + SHOULD try to finish as much of the original copy operation as + possible. + + If an error in executing the COPY method occurs with a resource other + than the resource identified in the Request-URI, then the response + MUST be a 207 (Multi-Status), and the URL of the resource causing the + failure MUST appear with the specific error. + + The 424 (Failed Dependency) status code SHOULD NOT be returned in the + 207 (Multi-Status) response from a COPY method. These responses can + be safely omitted because the client will know that the progeny of a + resource could not be copied when the client receives an error for + the parent. Additionally, 201 (Created)/204 (No Content) status + codes SHOULD NOT be returned as values in 207 (Multi-Status) + responses from COPY methods. They, too, can be safely omitted + because they are the default success codes. + +9.8.4. COPY and Overwriting Destination Resources + + If a COPY request has an Overwrite header with a value of "F", and a + resource exists at the Destination URL, the server MUST fail the + request. + + When a server executes a COPY request and overwrites a destination + resource, the exact behavior MAY depend on many factors, including + WebDAV extension capabilities (see particularly [RFC3253]). For + + + + + +Dusseault Standards Track [Page 53] + +RFC 4918 WebDAV June 2007 + + + example, when an ordinary resource is overwritten, the server could + delete the target resource before doing the copy, or could do an in- + place overwrite to preserve live properties. + + When a collection is overwritten, the membership of the destination + collection after the successful COPY request MUST be the same + membership as the source collection immediately before the COPY. + Thus, merging the membership of the source and destination + collections together in the destination is not a compliant behavior. + + In general, if clients require the state of the destination URL to be + wiped out prior to a COPY (e.g., to force live properties to be + reset), then the client could send a DELETE to the destination before + the COPY request to ensure this reset. + +9.8.5. Status Codes + + In addition to the general status codes possible, the following + status codes have specific applicability to COPY: + + 201 (Created) - The source resource was successfully copied. The + COPY operation resulted in the creation of a new resource. + + 204 (No Content) - The source resource was successfully copied to a + preexisting destination resource. + + 207 (Multi-Status) - Multiple resources were to be affected by the + COPY, but errors on some of them prevented the operation from taking + place. Specific error messages, together with the most appropriate + of the source and destination URLs, appear in the body of the multi- + status response. For example, if a destination resource was locked + and could not be overwritten, then the destination resource URL + appears with the 423 (Locked) status. + + 403 (Forbidden) - The operation is forbidden. A special case for + COPY could be that the source and destination resources are the same + resource. + + 409 (Conflict) - A resource cannot be created at the destination + until one or more intermediate collections have been created. The + server MUST NOT create those intermediate collections automatically. + + 412 (Precondition Failed) - A precondition header check failed, e.g., + the Overwrite header is "F" and the destination URL is already mapped + to a resource. + + + + + + +Dusseault Standards Track [Page 54] + +RFC 4918 WebDAV June 2007 + + + 423 (Locked) - The destination resource, or resource within the + destination collection, was locked. This response SHOULD contain the + 'lock-token-submitted' precondition element. + + 502 (Bad Gateway) - This may occur when the destination is on another + server, repository, or URL namespace. Either the source namespace + does not support copying to the destination namespace, or the + destination namespace refuses to accept the resource. The client may + wish to try GET/PUT and PROPFIND/PROPPATCH instead. + + 507 (Insufficient Storage) - The destination resource does not have + sufficient space to record the state of the resource after the + execution of this method. + +9.8.6. Example - COPY with Overwrite + + This example shows resource + http://www.example.com/~fielding/index.html being copied to the + location http://www.example.com/users/f/fielding/index.html. The 204 + (No Content) status code indicates that the existing resource at the + destination was overwritten. + + >>Request + + COPY /~fielding/index.html HTTP/1.1 + Host: www.example.com + Destination: http://www.example.com/users/f/fielding/index.html + + >>Response + + HTTP/1.1 204 No Content + +9.8.7. Example - COPY with No Overwrite + + The following example shows the same copy operation being performed, + but with the Overwrite header set to "F." A response of 412 + (Precondition Failed) is returned because the destination URL is + already mapped to a resource. + + >>Request + + COPY /~fielding/index.html HTTP/1.1 + Host: www.example.com + Destination: http://www.example.com/users/f/fielding/index.html + Overwrite: F + + + + + + +Dusseault Standards Track [Page 55] + +RFC 4918 WebDAV June 2007 + + + >>Response + + HTTP/1.1 412 Precondition Failed + +9.8.8. Example - COPY of a Collection + + >>Request + + COPY /container/ HTTP/1.1 + Host: www.example.com + Destination: http://www.example.com/othercontainer/ + Depth: infinity + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + http://www.example.com/othercontainer/R2/ + HTTP/1.1 423 Locked + + + + + The Depth header is unnecessary as the default behavior of COPY on a + collection is to act as if a "Depth: infinity" header had been + submitted. In this example, most of the resources, along with the + collection, were copied successfully. However, the collection R2 + failed because the destination R2 is locked. Because there was an + error copying R2, none of R2's members were copied. However, no + errors were listed for those members due to the error minimization + rules. + +9.9. MOVE Method + + The MOVE operation on a non-collection resource is the logical + equivalent of a copy (COPY), followed by consistency maintenance + processing, followed by a delete of the source, where all three + actions are performed in a single operation. The consistency + maintenance step allows the server to perform updates caused by the + move, such as updating all URLs, other than the Request-URI that + identifies the source resource, to point to the new destination + resource. + + + +Dusseault Standards Track [Page 56] + +RFC 4918 WebDAV June 2007 + + + The Destination header MUST be present on all MOVE methods and MUST + follow all COPY requirements for the COPY part of the MOVE method. + All WebDAV-compliant resources MUST support the MOVE method. + + Support for the MOVE method does not guarantee the ability to move a + resource to a particular destination. For example, separate programs + may actually control different sets of resources on the same server. + Therefore, it may not be possible to move a resource within a + namespace that appears to belong to the same server. + + If a resource exists at the destination, the destination resource + will be deleted as a side-effect of the MOVE operation, subject to + the restrictions of the Overwrite header. + + This method is idempotent, but not safe (see Section 9.1 of + [RFC2616]). Responses to this method MUST NOT be cached. + +9.9.1. MOVE for Properties + + Live properties described in this document SHOULD be moved along with + the resource, such that the resource has identically behaving live + properties at the destination resource, but not necessarily with the + same values. Note that some live properties are defined such that + the absence of the property has a specific meaning (e.g., a flag with + one meaning if present, and the opposite if absent), and in these + cases, a successful MOVE might result in the property being reported + as "Not Found" in subsequent requests. If the live properties will + not work the same way at the destination, the server MAY fail the + request. + + MOVE is frequently used by clients to rename a file without changing + its parent collection, so it's not appropriate to reset all live + properties that are set at resource creation. For example, the DAV: + creationdate property value SHOULD remain the same after a MOVE. + + Dead properties MUST be moved along with the resource. + +9.9.2. MOVE for Collections + + A MOVE with "Depth: infinity" instructs that the collection + identified by the Request-URI be moved to the address specified in + the Destination header, and all resources identified by its internal + member URLs are to be moved to locations relative to it, recursively + through all levels of the collection hierarchy. + + The MOVE method on a collection MUST act as if a "Depth: infinity" + header was used on it. A client MUST NOT submit a Depth header on a + MOVE on a collection with any value but "infinity". + + + +Dusseault Standards Track [Page 57] + +RFC 4918 WebDAV June 2007 + + + Any headers included with MOVE MUST be applied in processing every + resource to be moved with the exception of the Destination header. + The behavior of the Destination header is the same as given for COPY + on collections. + + When the MOVE method has completed processing, it MUST have created a + consistent URL namespace at both the source and destination (see + Section 5.1 for the definition of namespace consistency). However, + if an error occurs while moving an internal collection, the server + MUST NOT move any resources identified by members of the failed + collection (i.e., the server must skip the error-causing subtree), as + this would create an inconsistent namespace. In this case, after + detecting the error, the move operation SHOULD try to finish as much + of the original move as possible (i.e., the server should still + attempt to move other subtrees and the resources identified by their + members that are not descendants of an error-causing collection). + So, for example, if an infinite-depth move is performed on collection + /a/, which contains collections /a/b/ and /a/c/, and an error occurs + moving /a/b/, an attempt should still be made to try moving /a/c/. + Similarly, after encountering an error moving a non-collection + resource as part of an infinite-depth move, the server SHOULD try to + finish as much of the original move operation as possible. + + If an error occurs with a resource other than the resource identified + in the Request-URI, then the response MUST be a 207 (Multi-Status), + and the errored resource's URL MUST appear with the specific error. + + The 424 (Failed Dependency) status code SHOULD NOT be returned in the + 207 (Multi-Status) response from a MOVE method. These errors can be + safely omitted because the client will know that the progeny of a + resource could not be moved when the client receives an error for the + parent. Additionally, 201 (Created)/204 (No Content) responses + SHOULD NOT be returned as values in 207 (Multi-Status) responses from + a MOVE. These responses can be safely omitted because they are the + default success codes. + +9.9.3. MOVE and the Overwrite Header + + If a resource exists at the destination and the Overwrite header is + "T", then prior to performing the move, the server MUST perform a + DELETE with "Depth: infinity" on the destination resource. If the + Overwrite header is set to "F", then the operation will fail. + + + + + + + + + +Dusseault Standards Track [Page 58] + +RFC 4918 WebDAV June 2007 + + +9.9.4. Status Codes + + In addition to the general status codes possible, the following + status codes have specific applicability to MOVE: + + 201 (Created) - The source resource was successfully moved, and a new + URL mapping was created at the destination. + + 204 (No Content) - The source resource was successfully moved to a + URL that was already mapped. + + 207 (Multi-Status) - Multiple resources were to be affected by the + MOVE, but errors on some of them prevented the operation from taking + place. Specific error messages, together with the most appropriate + of the source and destination URLs, appear in the body of the multi- + status response. For example, if a source resource was locked and + could not be moved, then the source resource URL appears with the 423 + (Locked) status. + + 403 (Forbidden) - Among many possible reasons for forbidding a MOVE + operation, this status code is recommended for use when the source + and destination resources are the same. + + 409 (Conflict) - A resource cannot be created at the destination + until one or more intermediate collections have been created. The + server MUST NOT create those intermediate collections automatically. + Or, the server was unable to preserve the behavior of the live + properties and still move the resource to the destination (see + 'preserved-live-properties' postcondition). + + 412 (Precondition Failed) - A condition header failed. Specific to + MOVE, this could mean that the Overwrite header is "F" and the + destination URL is already mapped to a resource. + + 423 (Locked) - The source or the destination resource, the source or + destination resource parent, or some resource within the source or + destination collection, was locked. This response SHOULD contain the + 'lock-token-submitted' precondition element. + + 502 (Bad Gateway) - This may occur when the destination is on another + server and the destination server refuses to accept the resource. + This could also occur when the destination is on another sub-section + of the same server namespace. + + + + + + + + +Dusseault Standards Track [Page 59] + +RFC 4918 WebDAV June 2007 + + +9.9.5. Example - MOVE of a Non-Collection + + This example shows resource + http://www.example.com/~fielding/index.html being moved to the + location http://www.example.com/users/f/fielding/index.html. The + contents of the destination resource would have been overwritten if + the destination URL was already mapped to a resource. In this case, + since there was nothing at the destination resource, the response + code is 201 (Created). + + >>Request + + MOVE /~fielding/index.html HTTP/1.1 + Host: www.example.com + Destination: http://www.example/users/f/fielding/index.html + + >>Response + + HTTP/1.1 201 Created + Location: http://www.example.com/users/f/fielding/index.html + +9.9.6. Example - MOVE of a Collection + + >>Request + + MOVE /container/ HTTP/1.1 + Host: www.example.com + Destination: http://www.example.com/othercontainer/ + Overwrite: F + If: () + () + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.example.com/othercontainer/C2/ + HTTP/1.1 423 Locked + + + + + + + + +Dusseault Standards Track [Page 60] + +RFC 4918 WebDAV June 2007 + + + In this example, the client has submitted a number of lock tokens + with the request. A lock token will need to be submitted for every + resource, both source and destination, anywhere in the scope of the + method, that is locked. In this case, the proper lock token was not + submitted for the destination + http://www.example.com/othercontainer/C2/. This means that the + resource /container/C2/ could not be moved. Because there was an + error moving /container/C2/, none of /container/C2's members were + moved. However, no errors were listed for those members due to the + error minimization rules. User agent authentication has previously + occurred via a mechanism outside the scope of the HTTP protocol, in + an underlying transport layer. + +9.10. LOCK Method + + The following sections describe the LOCK method, which is used to + take out a lock of any access type and to refresh an existing lock. + These sections on the LOCK method describe only those semantics that + are specific to the LOCK method and are independent of the access + type of the lock being requested. + + Any resource that supports the LOCK method MUST, at minimum, support + the XML request and response formats defined herein. + + This method is neither idempotent nor safe (see Section 9.1 of + [RFC2616]). Responses to this method MUST NOT be cached. + +9.10.1. Creating a Lock on an Existing Resource + + A LOCK request to an existing resource will create a lock on the + resource identified by the Request-URI, provided the resource is not + already locked with a conflicting lock. The resource identified in + the Request-URI becomes the root of the lock. LOCK method requests + to create a new lock MUST have an XML request body. The server MUST + preserve the information provided by the client in the 'owner' + element in the LOCK request. The LOCK request MAY have a Timeout + header. + + When a new lock is created, the LOCK response: + + o MUST contain a body with the value of the DAV:lockdiscovery + property in a prop XML element. This MUST contain the full + information about the lock just granted, while information about + other (shared) locks is OPTIONAL. + + o MUST include the Lock-Token response header with the token + associated with the new lock. + + + + +Dusseault Standards Track [Page 61] + +RFC 4918 WebDAV June 2007 + + +9.10.2. Refreshing Locks + + A lock is refreshed by sending a LOCK request to the URL of a + resource within the scope of the lock. This request MUST NOT have a + body and it MUST specify which lock to refresh by using the 'If' + header with a single lock token (only one lock may be refreshed at a + time). The request MAY contain a Timeout header, which a server MAY + accept to change the duration remaining on the lock to the new value. + A server MUST ignore the Depth header on a LOCK refresh. + + If the resource has other (shared) locks, those locks are unaffected + by a lock refresh. Additionally, those locks do not prevent the + named lock from being refreshed. + + The Lock-Token header is not returned in the response for a + successful refresh LOCK request, but the LOCK response body MUST + contain the new value for the DAV:lockdiscovery property. + +9.10.3. Depth and Locking + + The Depth header may be used with the LOCK method. Values other than + 0 or infinity MUST NOT be used with the Depth header on a LOCK + method. All resources that support the LOCK method MUST support the + Depth header. + + A Depth header of value 0 means to just lock the resource specified + by the Request-URI. + + If the Depth header is set to infinity, then the resource specified + in the Request-URI along with all its members, all the way down the + hierarchy, are to be locked. A successful result MUST return a + single lock token. Similarly, if an UNLOCK is successfully executed + on this token, all associated resources are unlocked. Hence, partial + success is not an option for LOCK or UNLOCK. Either the entire + hierarchy is locked or no resources are locked. + + If the lock cannot be granted to all resources, the server MUST + return a Multi-Status response with a 'response' element for at least + one resource that prevented the lock from being granted, along with a + suitable status code for that failure (e.g., 403 (Forbidden) or 423 + (Locked)). Additionally, if the resource causing the failure was not + the resource requested, then the server SHOULD include a 'response' + element for the Request-URI as well, with a 'status' element + containing 424 Failed Dependency. + + If no Depth header is submitted on a LOCK request, then the request + MUST act as if a "Depth:infinity" had been submitted. + + + + +Dusseault Standards Track [Page 62] + +RFC 4918 WebDAV June 2007 + + +9.10.4. Locking Unmapped URLs + + A successful LOCK method MUST result in the creation of an empty + resource that is locked (and that is not a collection) when a + resource did not previously exist at that URL. Later on, the lock + may go away but the empty resource remains. Empty resources MUST + then appear in PROPFIND responses including that URL in the response + scope. A server MUST respond successfully to a GET request to an + empty resource, either by using a 204 No Content response, or by + using 200 OK with a Content-Length header indicating zero length + +9.10.5. Lock Compatibility Table + + The table below describes the behavior that occurs when a lock + request is made on a resource. + + +--------------------------+----------------+-------------------+ + | Current State | Shared Lock OK | Exclusive Lock OK | + +--------------------------+----------------+-------------------+ + | None | True | True | + | Shared Lock | True | False | + | Exclusive Lock | False | False* | + +--------------------------+----------------+-------------------+ + + Legend: True = lock may be granted. False = lock MUST NOT be + granted. *=It is illegal for a principal to request the same lock + twice. + + The current lock state of a resource is given in the leftmost column, + and lock requests are listed in the first row. The intersection of a + row and column gives the result of a lock request. For example, if a + shared lock is held on a resource, and an exclusive lock is + requested, the table entry is "false", indicating that the lock must + not be granted. + +9.10.6. LOCK Responses + + In addition to the general status codes possible, the following + status codes have specific applicability to LOCK: + + 200 (OK) - The LOCK request succeeded and the value of the DAV: + lockdiscovery property is included in the response body. + + 201 (Created) - The LOCK request was to an unmapped URL, the request + succeeded and resulted in the creation of a new resource, and the + value of the DAV:lockdiscovery property is included in the response + body. + + + + +Dusseault Standards Track [Page 63] + +RFC 4918 WebDAV June 2007 + + + 409 (Conflict) - A resource cannot be created at the destination + until one or more intermediate collections have been created. The + server MUST NOT create those intermediate collections automatically. + + 423 (Locked), potentially with 'no-conflicting-lock' precondition + code - There is already a lock on the resource that is not compatible + with the requested lock (see lock compatibility table above). + + 412 (Precondition Failed), with 'lock-token-matches-request-uri' + precondition code - The LOCK request was made with an If header, + indicating that the client wishes to refresh the given lock. + However, the Request-URI did not fall within the scope of the lock + identified by the token. The lock may have a scope that does not + include the Request-URI, or the lock could have disappeared, or the + token may be invalid. + +9.10.7. Example - Simple Lock Request + + >>Request + + LOCK /workspace/webdav/proposal.doc HTTP/1.1 + Host: example.com + Timeout: Infinite, Second-4100000000 + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + Authorization: Digest username="ejw", + realm="ejw@example.com", nonce="...", + uri="/workspace/webdav/proposal.doc", + response="...", opaque="..." + + + + + + + http://example.org/~ejw/contact.html + + + + >>Response + + HTTP/1.1 200 OK + Lock-Token: + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + +Dusseault Standards Track [Page 64] + +RFC 4918 WebDAV June 2007 + + + + + + + infinity + + http://example.org/~ejw/contact.html + + Second-604800 + + urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4 + + + http://example.com/workspace/webdav/proposal.doc + + + + + + + This example shows the successful creation of an exclusive write lock + on resource http://example.com/workspace/webdav/proposal.doc. The + resource http://example.org/~ejw/contact.html contains contact + information for the creator of the lock. The server has an activity- + based timeout policy in place on this resource, which causes the lock + to automatically be removed after 1 week (604800 seconds). Note that + the nonce, response, and opaque fields have not been calculated in + the Authorization request header. + +9.10.8. Example - Refreshing a Write Lock + + >>Request + + LOCK /workspace/webdav/proposal.doc HTTP/1.1 + Host: example.com + Timeout: Infinite, Second-4100000000 + If: () + Authorization: Digest username="ejw", + realm="ejw@example.com", nonce="...", + uri="/workspace/webdav/proposal.doc", + response="...", opaque="..." + + + + + + + + +Dusseault Standards Track [Page 65] + +RFC 4918 WebDAV June 2007 + + + >>Response + + HTTP/1.1 200 OK + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + infinity + + http://example.org/~ejw/contact.html + + Second-604800 + + urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4 + + + http://example.com/workspace/webdav/proposal.doc + + + + + + + This request would refresh the lock, attempting to reset the timeout + to the new value specified in the timeout header. Notice that the + client asked for an infinite time out but the server choose to ignore + the request. In this example, the nonce, response, and opaque fields + have not been calculated in the Authorization request header. + +9.10.9. Example - Multi-Resource Lock Request + + >>Request + + LOCK /webdav/ HTTP/1.1 + Host: example.com + Timeout: Infinite, Second-4100000000 + Depth: infinity + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + Authorization: Digest username="ejw", + realm="ejw@example.com", nonce="...", + + + +Dusseault Standards Track [Page 66] + +RFC 4918 WebDAV June 2007 + + + uri="/workspace/webdav/proposal.doc", + response="...", opaque="..." + + + + + + + http://example.org/~ejw/contact.html + + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://example.com/webdav/secret + HTTP/1.1 403 Forbidden + + + http://example.com/webdav/ + HTTP/1.1 424 Failed Dependency + + + + + This example shows a request for an exclusive write lock on a + collection and all its children. In this request, the client has + specified that it desires an infinite-length lock, if available, + otherwise a timeout of 4.1 billion seconds, if available. The + request entity body contains the contact information for the + principal taking out the lock -- in this case, a Web page URL. + + The error is a 403 (Forbidden) response on the resource + http://example.com/webdav/secret. Because this resource could not be + locked, none of the resources were locked. Note also that the a + 'response' element for the Request-URI itself has been included as + required. + + In this example, the nonce, response, and opaque fields have not been + calculated in the Authorization request header. + + + + + +Dusseault Standards Track [Page 67] + +RFC 4918 WebDAV June 2007 + + +9.11. UNLOCK Method + + The UNLOCK method removes the lock identified by the lock token in + the Lock-Token request header. The Request-URI MUST identify a + resource within the scope of the lock. + + Note that use of the Lock-Token header to provide the lock token is + not consistent with other state-changing methods, which all require + an If header with the lock token. Thus, the If header is not needed + to provide the lock token. Naturally, when the If header is present, + it has its normal meaning as a conditional header. + + For a successful response to this method, the server MUST delete the + lock entirely. + + If all resources that have been locked under the submitted lock token + cannot be unlocked, then the UNLOCK request MUST fail. + + A successful response to an UNLOCK method does not mean that the + resource is necessarily unlocked. It means that the specific lock + corresponding to the specified token no longer exists. + + Any DAV-compliant resource that supports the LOCK method MUST support + the UNLOCK method. + + This method is idempotent, but not safe (see Section 9.1 of + [RFC2616]). Responses to this method MUST NOT be cached. + +9.11.1. Status Codes + + In addition to the general status codes possible, the following + status codes have specific applicability to UNLOCK: + + 204 (No Content) - Normal success response (rather than 200 OK, since + 200 OK would imply a response body, and an UNLOCK success response + does not normally contain a body). + + 400 (Bad Request) - No lock token was provided. + + 403 (Forbidden) - The currently authenticated principal does not have + permission to remove the lock. + + 409 (Conflict), with 'lock-token-matches-request-uri' precondition - + The resource was not locked, or the request was made to a Request-URI + that was not within the scope of the lock. + + + + + + +Dusseault Standards Track [Page 68] + +RFC 4918 WebDAV June 2007 + + +9.11.2. Example - UNLOCK + + >>Request + + UNLOCK /workspace/webdav/info.doc HTTP/1.1 + Host: example.com + Lock-Token: + Authorization: Digest username="ejw" + realm="ejw@example.com", nonce="...", + uri="/workspace/webdav/proposal.doc", + response="...", opaque="..." + + >>Response + + HTTP/1.1 204 No Content + + In this example, the lock identified by the lock token + "urn:uuid:a515cfa4-5da4-22e1-f5b5-00a0451e6bf7" is successfully + removed from the resource + http://example.com/workspace/webdav/info.doc. If this lock included + more than just one resource, the lock is removed from all resources + included in the lock. + + In this example, the nonce, response, and opaque fields have not been + calculated in the Authorization request header. + +10. HTTP Headers for Distributed Authoring + + All DAV headers follow the same basic formatting rules as HTTP + headers. This includes rules like line continuation and how to + combine (or separate) multiple instances of the same header using + commas. + + WebDAV adds two new conditional headers to the set defined in HTTP: + the If and Overwrite headers. + +10.1. DAV Header + + DAV = "DAV" ":" #( compliance-class ) + compliance-class = ( "1" | "2" | "3" | extend ) + extend = Coded-URL | token + ; token is defined in RFC 2616, Section 2.2 + Coded-URL = "<" absolute-URI ">" + ; No linear whitespace (LWS) allowed in Coded-URL + ; absolute-URI defined in RFC 3986, Section 4.3 + + + + + + +Dusseault Standards Track [Page 69] + +RFC 4918 WebDAV June 2007 + + + This general-header appearing in the response indicates that the + resource supports the DAV schema and protocol as specified. All DAV- + compliant resources MUST return the DAV header with compliance-class + "1" on all OPTIONS responses. In cases where WebDAV is only + supported in part of the server namespace, an OPTIONS request to non- + WebDAV resources (including "/") SHOULD NOT advertise WebDAV support. + + The value is a comma-separated list of all compliance class + identifiers that the resource supports. Class identifiers may be + Coded-URLs or tokens (as defined by [RFC2616]). Identifiers can + appear in any order. Identifiers that are standardized through the + IETF RFC process are tokens, but other identifiers SHOULD be Coded- + URLs to encourage uniqueness. + + A resource must show class 1 compliance if it shows class 2 or 3 + compliance. In general, support for one compliance class does not + entail support for any other, and in particular, support for + compliance class 3 does not require support for compliance class 2. + Please refer to Section 18 for more details on compliance classes + defined in this specification. + + Note that many WebDAV servers do not advertise WebDAV support in + response to "OPTIONS *". + + As a request header, this header allows the client to advertise + compliance with named features when the server needs that + information. Clients SHOULD NOT send this header unless a standards + track specification requires it. Any extension that makes use of + this as a request header will need to carefully consider caching + implications. + +10.2. Depth Header + + Depth = "Depth" ":" ("0" | "1" | "infinity") + + The Depth request header is used with methods executed on resources + that could potentially have internal members to indicate whether the + method is to be applied only to the resource ("Depth: 0"), to the + resource and its internal members only ("Depth: 1"), or the resource + and all its members ("Depth: infinity"). + + The Depth header is only supported if a method's definition + explicitly provides for such support. + + The following rules are the default behavior for any method that + supports the Depth header. A method may override these defaults by + defining different behavior in its definition. + + + + +Dusseault Standards Track [Page 70] + +RFC 4918 WebDAV June 2007 + + + Methods that support the Depth header may choose not to support all + of the header's values and may define, on a case-by-case basis, the + behavior of the method if a Depth header is not present. For + example, the MOVE method only supports "Depth: infinity", and if a + Depth header is not present, it will act as if a "Depth: infinity" + header had been applied. + + Clients MUST NOT rely upon methods executing on members of their + hierarchies in any particular order or on the execution being atomic + unless the particular method explicitly provides such guarantees. + + Upon execution, a method with a Depth header will perform as much of + its assigned task as possible and then return a response specifying + what it was able to accomplish and what it failed to do. + + So, for example, an attempt to COPY a hierarchy may result in some of + the members being copied and some not. + + By default, the Depth header does not interact with other headers. + That is, each header on a request with a Depth header MUST be applied + only to the Request-URI if it applies to any resource, unless + specific Depth behavior is defined for that header. + + If a source or destination resource within the scope of the Depth + header is locked in such a way as to prevent the successful execution + of the method, then the lock token for that resource MUST be + submitted with the request in the If request header. + + The Depth header only specifies the behavior of the method with + regards to internal members. If a resource does not have internal + members, then the Depth header MUST be ignored. + +10.3. Destination Header + + The Destination request header specifies the URI that identifies a + destination resource for methods such as COPY and MOVE, which take + two URIs as parameters. + + Destination = "Destination" ":" Simple-ref + + + If the Destination value is an absolute-URI (Section 4.3 of + [RFC3986]), it may name a different server (or different port or + scheme). If the source server cannot attempt a copy to the remote + server, it MUST fail the request. Note that copying and moving + resources to remote servers is not fully defined in this + specification (e.g., specific error conditions). + + + + +Dusseault Standards Track [Page 71] + +RFC 4918 WebDAV June 2007 + + + If the Destination value is too long or otherwise unacceptable, the + server SHOULD return 400 (Bad Request), ideally with helpful + information in an error body. + +10.4. If Header + + The If request header is intended to have similar functionality to + the If-Match header defined in Section 14.24 of [RFC2616]. However, + the If header handles any state token as well as ETags. A typical + example of a state token is a lock token, and lock tokens are the + only state tokens defined in this specification. + +10.4.1. Purpose + + The If header has two distinct purposes: + + o The first purpose is to make a request conditional by supplying a + series of state lists with conditions that match tokens and ETags + to a specific resource. If this header is evaluated and all state + lists fail, then the request MUST fail with a 412 (Precondition + Failed) status. On the other hand, the request can succeed only + if one of the described state lists succeeds. The success + criteria for state lists and matching functions are defined in + Sections 10.4.3 and 10.4.4. + + o Additionally, the mere fact that a state token appears in an If + header means that it has been "submitted" with the request. In + general, this is used to indicate that the client has knowledge of + that state token. The semantics for submitting a state token + depend on its type (for lock tokens, please refer to Section 6). + + Note that these two purposes need to be treated distinctly: a state + token counts as being submitted independently of whether the server + actually has evaluated the state list it appears in, and also + independently of whether or not the condition it expressed was found + to be true. + +10.4.2. Syntax + + If = "If" ":" ( 1*No-tag-list | 1*Tagged-list ) + + No-tag-list = List + Tagged-list = Resource-Tag 1*List + + List = "(" 1*Condition ")" + Condition = ["Not"] (State-token | "[" entity-tag "]") + ; entity-tag: see Section 3.11 of [RFC2616] + ; No LWS allowed between "[", entity-tag and "]" + + + +Dusseault Standards Track [Page 72] + +RFC 4918 WebDAV June 2007 + + + State-token = Coded-URL + + Resource-Tag = "<" Simple-ref ">" + ; Simple-ref: see Section 8.3 + ; No LWS allowed in Resource-Tag + + The syntax distinguishes between untagged lists ("No-tag-list") and + tagged lists ("Tagged-list"). Untagged lists apply to the resource + identified by the Request-URI, while tagged lists apply to the + resource identified by the preceding Resource-Tag. + + A Resource-Tag applies to all subsequent Lists, up to the next + Resource-Tag. + + Note that the two list types cannot be mixed within an If header. + This is not a functional restriction because the No-tag-list syntax + is just a shorthand notation for a Tagged-list production with a + Resource-Tag referring to the Request-URI. + + Each List consists of one or more Conditions. Each Condition is + defined in terms of an entity-tag or state-token, potentially negated + by the prefix "Not". + + Note that the If header syntax does not allow multiple instances of + If headers in a single request. However, the HTTP header syntax + allows extending single header values across multiple lines, by + inserting a line break followed by whitespace (see [RFC2616], Section + 4.2). + +10.4.3. List Evaluation + + A Condition that consists of a single entity-tag or state-token + evaluates to true if the resource matches the described state (where + the individual matching functions are defined below in + Section 10.4.4). Prefixing it with "Not" reverses the result of the + evaluation (thus, the "Not" applies only to the subsequent entity-tag + or state-token). + + Each List production describes a series of conditions. The whole + list evaluates to true if and only if each condition evaluates to + true (that is, the list represents a logical conjunction of + Conditions). + + Each No-tag-list and Tagged-list production may contain one or more + Lists. They evaluate to true if and only if any of the contained + lists evaluates to true (that is, if there's more than one List, that + List sequence represents a logical disjunction of the Lists). + + + + +Dusseault Standards Track [Page 73] + +RFC 4918 WebDAV June 2007 + + + Finally, the whole If header evaluates to true if and only if at + least one of the No-tag-list or Tagged-list productions evaluates to + true. If the header evaluates to false, the server MUST reject the + request with a 412 (Precondition Failed) status. Otherwise, + execution of the request can proceed as if the header wasn't present. + +10.4.4. Matching State Tokens and ETags + + When performing If header processing, the definition of a matching + state token or entity tag is as follows: + + Identifying a resource: The resource is identified by the URI along + with the token, in tagged list production, or by the Request-URI in + untagged list production. + + Matching entity tag: Where the entity tag matches an entity tag + associated with the identified resource. Servers MUST use either the + weak or the strong comparison function defined in Section 13.3.3 of + [RFC2616]. + + Matching state token: Where there is an exact match between the state + token in the If header and any state token on the identified + resource. A lock state token is considered to match if the resource + is anywhere in the scope of the lock. + + Handling unmapped URLs: For both ETags and state tokens, treat as if + the URL identified a resource that exists but does not have the + specified state. + +10.4.5. If Header and Non-DAV-Aware Proxies + + Non-DAV-aware proxies will not honor the If header, since they will + not understand the If header, and HTTP requires non-understood + headers to be ignored. When communicating with HTTP/1.1 proxies, the + client MUST use the "Cache-Control: no-cache" request header so as to + prevent the proxy from improperly trying to service the request from + its cache. When dealing with HTTP/1.0 proxies, the "Pragma: no- + cache" request header MUST be used for the same reason. + + Because in general clients may not be able to reliably detect non- + DAV-aware intermediates, they are advised to always prevent caching + using the request directives mentioned above. + + + + + + + + + +Dusseault Standards Track [Page 74] + +RFC 4918 WebDAV June 2007 + + +10.4.6. Example - No-tag Production + + If: ( + ["I am an ETag"]) + (["I am another ETag"]) + + The previous header would require that the resource identified in the + Request-URI be locked with the specified lock token and be in the + state identified by the "I am an ETag" ETag or in the state + identified by the second ETag "I am another ETag". + + To put the matter more plainly one can think of the previous If + header as expressing the condition below: + + ( + is-locked-with(urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2) AND + matches-etag("I am an ETag") + ) + OR + ( + matches-etag("I am another ETag") + ) + +10.4.7. Example - Using "Not" with No-tag Production + + If: (Not + ) + + This If header requires that the resource must not be locked with a + lock having the lock token + urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2 and must be locked by a + lock with the lock token + urn:uuid:58f202ac-22cf-11d1-b12d-002035b29092. + +10.4.8. Example - Causing a Condition to Always Evaluate to True + + There may be cases where a client wishes to submit state tokens, but + doesn't want the request to fail just because the state token isn't + current anymore. One simple way to do this is to include a Condition + that is known to always evaluate to true, such as in: + + If: () + (Not ) + + "DAV:no-lock" is known to never represent a current lock token. Lock + tokens are assigned by the server, following the uniqueness + requirements described in Section 6.5, therefore cannot use the + "DAV:" scheme. Thus, by applying "Not" to a state token that is + + + +Dusseault Standards Track [Page 75] + +RFC 4918 WebDAV June 2007 + + + known not to be current, the Condition always evaluates to true. + Consequently, the whole If header will always evaluate to true, and + the lock token urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2 will be + submitted in any case. + +10.4.9. Example - Tagged List If Header in COPY + + >>Request + + COPY /resource1 HTTP/1.1 + Host: www.example.com + Destination: /resource2 + If: + ( + [W/"A weak ETag"]) (["strong ETag"]) + + In this example, http://www.example.com/resource1 is being copied to + http://www.example.com/resource2. When the method is first applied + to http://www.example.com/resource1, resource1 must be in the state + specified by "( [W/"A + weak ETag"]) (["strong ETag"])". That is, either it must be locked + with a lock token of "urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2" + and have a weak entity tag W/"A weak ETag" or it must have a strong + entity tag "strong ETag". + +10.4.10. Example - Matching Lock Tokens with Collection Locks + + DELETE /specs/rfc2518.txt HTTP/1.1 + Host: www.example.com + If: + () + + For this example, the lock token must be compared to the identified + resource, which is the 'specs' collection identified by the URL in + the tagged list production. If the 'specs' collection is not locked + by a lock with the specified lock token, the request MUST fail. + Otherwise, this request could succeed, because the If header + evaluates to true, and because the lock token for the lock affecting + the affected resource has been submitted. + +10.4.11. Example - Matching ETags on Unmapped URLs + + Consider a collection "/specs" that does not contain the member + "/specs/rfc2518.doc". In this case, the If header + + If: (["4217"]) + + + + + +Dusseault Standards Track [Page 76] + +RFC 4918 WebDAV June 2007 + + + will evaluate to false (the URI isn't mapped, thus the resource + identified by the URI doesn't have an entity matching the ETag + "4217"). + + On the other hand, an If header of + + If: (Not ["4217"]) + + will consequently evaluate to true. + + Note that, as defined above in Section 10.4.4, the same + considerations apply to matching state tokens. + +10.5. Lock-Token Header + + Lock-Token = "Lock-Token" ":" Coded-URL + + The Lock-Token request header is used with the UNLOCK method to + identify the lock to be removed. The lock token in the Lock-Token + request header MUST identify a lock that contains the resource + identified by Request-URI as a member. + + The Lock-Token response header is used with the LOCK method to + indicate the lock token created as a result of a successful LOCK + request to create a new lock. + +10.6. Overwrite Header + + Overwrite = "Overwrite" ":" ("T" | "F") + + The Overwrite request header specifies whether the server should + overwrite a resource mapped to the destination URL during a COPY or + MOVE. A value of "F" states that the server must not perform the + COPY or MOVE operation if the destination URL does map to a resource. + If the overwrite header is not included in a COPY or MOVE request, + then the resource MUST treat the request as if it has an overwrite + header of value "T". While the Overwrite header appears to duplicate + the functionality of using an "If-Match: *" header (see [RFC2616]), + If-Match applies only to the Request-URI, and not to the Destination + of a COPY or MOVE. + + If a COPY or MOVE is not performed due to the value of the Overwrite + header, the method MUST fail with a 412 (Precondition Failed) status + code. The server MUST do authorization checks before checking this + or any conditional header. + + All DAV-compliant resources MUST support the Overwrite header. + + + + +Dusseault Standards Track [Page 77] + +RFC 4918 WebDAV June 2007 + + +10.7. Timeout Request Header + + TimeOut = "Timeout" ":" 1#TimeType + TimeType = ("Second-" DAVTimeOutVal | "Infinite") + ; No LWS allowed within TimeType + DAVTimeOutVal = 1*DIGIT + + Clients MAY include Timeout request headers in their LOCK requests. + However, the server is not required to honor or even consider these + requests. Clients MUST NOT submit a Timeout request header with any + method other than a LOCK method. + + The "Second" TimeType specifies the number of seconds that will + elapse between granting of the lock at the server, and the automatic + removal of the lock. The timeout value for TimeType "Second" MUST + NOT be greater than 2^32-1. + + See Section 6.6 for a description of lock timeout behavior. + +11. Status Code Extensions to HTTP/1.1 + + The following status codes are added to those defined in HTTP/1.1 + [RFC2616]. + +11.1. 207 Multi-Status + + The 207 (Multi-Status) status code provides status for multiple + independent operations (see Section 13 for more information). + +11.2. 422 Unprocessable Entity + + The 422 (Unprocessable Entity) status code means the server + understands the content type of the request entity (hence a + 415(Unsupported Media Type) status code is inappropriate), and the + syntax of the request entity is correct (thus a 400 (Bad Request) + status code is inappropriate) but was unable to process the contained + instructions. For example, this error condition may occur if an XML + request body contains well-formed (i.e., syntactically correct), but + semantically erroneous, XML instructions. + +11.3. 423 Locked + + The 423 (Locked) status code means the source or destination resource + of a method is locked. This response SHOULD contain an appropriate + precondition or postcondition code, such as 'lock-token-submitted' or + 'no-conflicting-lock'. + + + + + +Dusseault Standards Track [Page 78] + +RFC 4918 WebDAV June 2007 + + +11.4. 424 Failed Dependency + + The 424 (Failed Dependency) status code means that the method could + not be performed on the resource because the requested action + depended on another action and that action failed. For example, if a + command in a PROPPATCH method fails, then, at minimum, the rest of + the commands will also fail with 424 (Failed Dependency). + +11.5. 507 Insufficient Storage + + The 507 (Insufficient Storage) status code means the method could not + be performed on the resource because the server is unable to store + the representation needed to successfully complete the request. This + condition is considered to be temporary. If the request that + received this status code was the result of a user action, the + request MUST NOT be repeated until it is requested by a separate user + action. + +12. Use of HTTP Status Codes + + These HTTP codes are not redefined, but their use is somewhat + extended by WebDAV methods and requirements. In general, many HTTP + status codes can be used in response to any request, not just in + cases described in this document. Note also that WebDAV servers are + known to use 300-level redirect responses (and early interoperability + tests found clients unprepared to see those responses). A 300-level + response MUST NOT be used when the server has created a new resource + in response to the request. + +12.1. 412 Precondition Failed + + Any request can contain a conditional header defined in HTTP (If- + Match, If-Modified-Since, etc.) or the "If" or "Overwrite" + conditional headers defined in this specification. If the server + evaluates a conditional header, and if that condition fails to hold, + then this error code MUST be returned. On the other hand, if the + client did not include a conditional header in the request, then the + server MUST NOT use this status code. + +12.2. 414 Request-URI Too Long + + This status code is used in HTTP 1.1 only for Request-URIs, not URIs + in other locations. + + + + + + + + +Dusseault Standards Track [Page 79] + +RFC 4918 WebDAV June 2007 + + +13. Multi-Status Response + + A Multi-Status response conveys information about multiple resources + in situations where multiple status codes might be appropriate. The + default Multi-Status response body is a text/xml or application/xml + HTTP entity with a 'multistatus' root element. Further elements + contain 200, 300, 400, and 500 series status codes generated during + the method invocation. 100 series status codes SHOULD NOT be recorded + in a 'response' XML element. + + Although '207' is used as the overall response status code, the + recipient needs to consult the contents of the multistatus response + body for further information about the success or failure of the + method execution. The response MAY be used in success, partial + success and also in failure situations. + + The 'multistatus' root element holds zero or more 'response' elements + in any order, each with information about an individual resource. + Each 'response' element MUST have an 'href' element to identify the + resource. + + A Multi-Status response uses one out of two distinct formats for + representing the status: + + 1. A 'status' element as child of the 'response' element indicates + the status of the message execution for the identified resource + as a whole (for instance, see Section 9.6.2). Some method + definitions provide information about specific status codes + clients should be prepared to see in a response. However, + clients MUST be able to handle other status codes, using the + generic rules defined in Section 10 of [RFC2616]. + + 2. For PROPFIND and PROPPATCH, the format has been extended using + the 'propstat' element instead of 'status', providing information + about individual properties of a resource. This format is + specific to PROPFIND and PROPPATCH, and is described in detail in + Sections 9.1 and 9.2. + +13.1. Response Headers + + HTTP defines the Location header to indicate a preferred URL for the + resource that was addressed in the Request-URI (e.g., in response to + successful PUT requests or in redirect responses). However, use of + this header creates ambiguity when there are URLs in the body of the + response, as with Multi-Status. Thus, use of the Location header + with the Multi-Status response is intentionally undefined. + + + + + +Dusseault Standards Track [Page 80] + +RFC 4918 WebDAV June 2007 + + +13.2. Handling Redirected Child Resources + + Redirect responses (300-303, 305, and 307) defined in HTTP 1.1 + normally take a Location header to indicate the new URI for the + single resource redirected from the Request-URI. Multi-Status + responses contain many resource addresses, but the original + definition in [RFC2518] did not have any place for the server to + provide the new URI for redirected resources. This specification + does define a 'location' element for this information (see + Section 14.9). Servers MUST use this new element with redirect + responses in Multi-Status. + + Clients encountering redirected resources in Multi-Status MUST NOT + rely on the 'location' element being present with a new URI. If the + element is not present, the client MAY reissue the request to the + individual redirected resource, because the response to that request + can be redirected with a Location header containing the new URI. + +13.3. Internal Status Codes + + Sections 9.2.1, 9.1.2, 9.6.1, 9.8.3, and 9.9.2 define various status + codes used in Multi-Status responses. This specification does not + define the meaning of other status codes that could appear in these + responses. + +14. XML Element Definitions + + In this section, the final line of each section gives the element + type declaration using the format defined in [REC-XML]. The "Value" + field, where present, specifies further restrictions on the allowable + contents of the XML element using BNF (i.e., to further restrict the + values of a PCDATA element). Note that all of the elements defined + here may be extended according to the rules defined in Section 17. + All elements defined here are in the "DAV:" namespace. + +14.1. activelock XML Element + + Name: activelock + + Purpose: Describes a lock on a resource. + + + + + + + + + + +Dusseault Standards Track [Page 81] + +RFC 4918 WebDAV June 2007 + + +14.2. allprop XML Element + + Name: allprop + + Purpose: Specifies that all names and values of dead properties and + the live properties defined by this document existing on the + resource are to be returned. + + + +14.3. collection XML Element + + Name: collection + + Purpose: Identifies the associated resource as a collection. The + DAV:resourcetype property of a collection resource MUST contain + this element. It is normally empty but extensions may add sub- + elements. + + + +14.4. depth XML Element + + Name: depth + + Purpose: Used for representing depth values in XML content (e.g., + in lock information). + + Value: "0" | "1" | "infinity" + + + +14.5. error XML Element + + Name: error + + Purpose: Error responses, particularly 403 Forbidden and 409 + Conflict, sometimes need more information to indicate what went + wrong. In these cases, servers MAY return an XML response body + with a document element of 'error', containing child elements + identifying particular condition codes. + + Description: Contains at least one XML element, and MUST NOT + contain text or mixed content. Any element that is a child of the + 'error' element is considered to be a precondition or + postcondition code. Unrecognized elements MUST be ignored. + + + + + +Dusseault Standards Track [Page 82] + +RFC 4918 WebDAV June 2007 + + +14.6. exclusive XML Element + + Name: exclusive + + Purpose: Specifies an exclusive lock. + + + + + +14.7. href XML Element + + Name: href + + Purpose: MUST contain a URI or a relative reference. + + Description: There may be limits on the value of 'href' depending + on the context of its use. Refer to the specification text where + 'href' is used to see what limitations apply in each case. + + Value: Simple-ref + + + + +14.8. include XML Element + + Name: include + + Purpose: Any child element represents the name of a property to be + included in the PROPFIND response. All elements inside an + 'include' XML element MUST define properties related to the + resource, although possible property names are in no way limited + to those property names defined in this document or other + standards. This element MUST NOT contain text or mixed content. + + + +14.9. location XML Element + + Name: location + + Purpose: HTTP defines the "Location" header (see [RFC2616], Section + 14.30) for use with some status codes (such as 201 and the 300 + series codes). When these codes are used inside a 'multistatus' + element, the 'location' element can be used to provide the + accompanying Location header value. + + + + +Dusseault Standards Track [Page 83] + +RFC 4918 WebDAV June 2007 + + + Description: Contains a single href element with the same value + that would be used in a Location header. + + + + +14.10. lockentry XML Element + + Name: lockentry + + Purpose: Defines the types of locks that can be used with the + resource. + + + +14.11. lockinfo XML Element + + Name: lockinfo + + Purpose: The 'lockinfo' XML element is used with a LOCK method to + specify the type of lock the client wishes to have created. + + + + +14.12. lockroot XML Element + + Name: lockroot + + Purpose: Contains the root URL of the lock, which is the URL + through which the resource was addressed in the LOCK request. + + Description: The href element contains the root of the lock. The + server SHOULD include this in all DAV:lockdiscovery property + values and the response to LOCK requests. + + + +14.13. lockscope XML Element + + Name: lockscope + + Purpose: Specifies whether a lock is an exclusive lock, or a shared + lock. + + + + + + + +Dusseault Standards Track [Page 84] + +RFC 4918 WebDAV June 2007 + + +14.14. locktoken XML Element + + Name: locktoken + + Purpose: The lock token associated with a lock. + + Description: The href contains a single lock token URI, which + refers to the lock. + + + +14.15. locktype XML Element + + Name: locktype + + Purpose: Specifies the access type of a lock. At present, this + specification only defines one lock type, the write lock. + + + + + +14.16. multistatus XML Element + + Name: multistatus + + Purpose: Contains multiple response messages. + + Description: The 'responsedescription' element at the top level is + used to provide a general message describing the overarching + nature of the response. If this value is available, an + application may use it instead of presenting the individual + response descriptions contained within the responses. + + + + + +14.17. owner XML Element + + Name: owner + + Purpose: Holds client-supplied information about the creator of a + lock. + + Description: Allows a client to provide information sufficient for + either directly contacting a principal (such as a telephone number + or Email URI), or for discovering the principal (such as the URL + + + +Dusseault Standards Track [Page 85] + +RFC 4918 WebDAV June 2007 + + + of a homepage) who created a lock. The value provided MUST be + treated as a dead property in terms of XML Information Item + preservation. The server MUST NOT alter the value unless the + owner value provided by the client is empty. For a certain amount + of interoperability between different client implementations, if + clients have URI-formatted contact information for the lock + creator suitable for user display, then clients SHOULD put those + URIs in 'href' child elements of the 'owner' element. + + Extensibility: MAY be extended with child elements, mixed content, + text content or attributes. + + + +14.18. prop XML Element + + Name: prop + + Purpose: Contains properties related to a resource. + + Description: A generic container for properties defined on + resources. All elements inside a 'prop' XML element MUST define + properties related to the resource, although possible property + names are in no way limited to those property names defined in + this document or other standards. This element MUST NOT contain + text or mixed content. + + + +14.19. propertyupdate XML Element + + Name: propertyupdate + + Purpose: Contains a request to alter the properties on a resource. + + Description: This XML element is a container for the information + required to modify the properties on the resource. + + + +14.20. propfind XML Element + + Name: propfind + + + + + + + + +Dusseault Standards Track [Page 86] + +RFC 4918 WebDAV June 2007 + + + Purpose: Specifies the properties to be returned from a PROPFIND + method. Four special elements are specified for use with + 'propfind': 'prop', 'allprop', 'include', and 'propname'. If + 'prop' is used inside 'propfind', it MUST NOT contain property + values. + + + +14.21. propname XML Element + + Name: propname + + Purpose: Specifies that only a list of property names on the + resource is to be returned. + + + +14.22. propstat XML Element + + Name: propstat + + Purpose: Groups together a prop and status element that is + associated with a particular 'href' element. + + Description: The propstat XML element MUST contain one prop XML + element and one status XML element. The contents of the prop XML + element MUST only list the names of properties to which the result + in the status element applies. The optional precondition/ + postcondition element and 'responsedescription' text also apply to + the properties named in 'prop'. + + + +14.23. remove XML Element + + Name: remove + + Purpose: Lists the properties to be removed from a resource. + + Description: Remove instructs that the properties specified in prop + should be removed. Specifying the removal of a property that does + not exist is not an error. All the XML elements in a 'prop' XML + element inside of a 'remove' XML element MUST be empty, as only + the names of properties to be removed are required. + + + + + + + +Dusseault Standards Track [Page 87] + +RFC 4918 WebDAV June 2007 + + +14.24. response XML Element + + Name: response + + Purpose: Holds a single response describing the effect of a method + on resource and/or its properties. + + Description: The 'href' element contains an HTTP URL pointing to a + WebDAV resource when used in the 'response' container. A + particular 'href' value MUST NOT appear more than once as the + child of a 'response' XML element under a 'multistatus' XML + element. This requirement is necessary in order to keep + processing costs for a response to linear time. Essentially, this + prevents having to search in order to group together all the + responses by 'href'. There are, however, no requirements + regarding ordering based on 'href' values. The optional + precondition/postcondition element and 'responsedescription' text + can provide additional information about this resource relative to + the request or result. + + + + +14.25. responsedescription XML Element + + Name: responsedescription + + Purpose: Contains information about a status response within a + Multi-Status. + + Description: Provides information suitable to be presented to a + user. + + + +14.26. set XML Element + + Name: set + + Purpose: Lists the property values to be set for a resource. + + Description: The 'set' element MUST contain only a 'prop' element. + The elements contained by the 'prop' element inside the 'set' + element MUST specify the name and value of properties that are set + on the resource identified by Request-URI. If a property already + exists, then its value is replaced. Language tagging information + appearing in the scope of the 'prop' element (in the "xml:lang" + + + +Dusseault Standards Track [Page 88] + +RFC 4918 WebDAV June 2007 + + + attribute, if present) MUST be persistently stored along with the + property, and MUST be subsequently retrievable using PROPFIND. + + + +14.27. shared XML Element + + Name: shared + + Purpose: Specifies a shared lock. + + + + + +14.28. status XML Element + + Name: status + + Purpose: Holds a single HTTP status-line. + + Value: status-line (defined in Section 6.1 of [RFC2616]) + + + +14.29. timeout XML Element + + Name: timeout + + Purpose: The number of seconds remaining before a lock expires. + + Value: TimeType (defined in Section 10.7) + + + + +14.30. write XML Element + + Name: write + + Purpose: Specifies a write lock. + + + + + + + + + + +Dusseault Standards Track [Page 89] + +RFC 4918 WebDAV June 2007 + + +15. DAV Properties + + For DAV properties, the name of the property is also the same as the + name of the XML element that contains its value. In the section + below, the final line of each section gives the element type + declaration using the format defined in [REC-XML]. The "Value" + field, where present, specifies further restrictions on the allowable + contents of the XML element using BNF (i.e., to further restrict the + values of a PCDATA element). + + A protected property is one that cannot be changed with a PROPPATCH + request. There may be other requests that would result in a change + to a protected property (as when a LOCK request affects the value of + DAV:lockdiscovery). Note that a given property could be protected on + one type of resource, but not protected on another type of resource. + + A computed property is one with a value defined in terms of a + computation (based on the content and other properties of that + resource, or even of some other resource). A computed property is + always a protected property. + + COPY and MOVE behavior refers to local COPY and MOVE operations. + + For properties defined based on HTTP GET response headers (DAV:get*), + the header value could include LWS as defined in [RFC2616], Section + 4.2. Server implementors SHOULD strip LWS from these values before + using as WebDAV property values. + +15.1. creationdate Property + + Name: creationdate + + Purpose: Records the time and date the resource was created. + + Value: date-time (defined in [RFC3339], see the ABNF in Section + 5.6.) + + Protected: MAY be protected. Some servers allow DAV:creationdate + to be changed to reflect the time the document was created if that + is more meaningful to the user (rather than the time it was + uploaded). Thus, clients SHOULD NOT use this property in + synchronization logic (use DAV:getetag instead). + + COPY/MOVE behavior: This property value SHOULD be kept during a + MOVE operation, but is normally re-initialized when a resource is + created with a COPY. It should not be set in a COPY. + + + + + +Dusseault Standards Track [Page 90] + +RFC 4918 WebDAV June 2007 + + + Description: The DAV:creationdate property SHOULD be defined on all + DAV compliant resources. If present, it contains a timestamp of + the moment when the resource was created. Servers that are + incapable of persistently recording the creation date SHOULD + instead leave it undefined (i.e. report "Not Found"). + + + +15.2. displayname Property + + Name: displayname + + Purpose: Provides a name for the resource that is suitable for + presentation to a user. + + Value: Any text. + + Protected: SHOULD NOT be protected. Note that servers implementing + [RFC2518] might have made this a protected property as this is a + new requirement. + + COPY/MOVE behavior: This property value SHOULD be preserved in COPY + and MOVE operations. + + Description: Contains a description of the resource that is + suitable for presentation to a user. This property is defined on + the resource, and hence SHOULD have the same value independent of + the Request-URI used to retrieve it (thus, computing this property + based on the Request-URI is deprecated). While generic clients + might display the property value to end users, client UI designers + must understand that the method for identifying resources is still + the URL. Changes to DAV:displayname do not issue moves or copies + to the server, but simply change a piece of meta-data on the + individual resource. Two resources can have the same DAV: + displayname value even within the same collection. + + + +15.3. getcontentlanguage Property + + Name: getcontentlanguage + + Purpose: Contains the Content-Language header value (from Section + 14.12 of [RFC2616]) as it would be returned by a GET without + accept headers. + + Value: language-tag (language-tag is defined in Section 3.10 of + [RFC2616]) + + + +Dusseault Standards Track [Page 91] + +RFC 4918 WebDAV June 2007 + + + Protected: SHOULD NOT be protected, so that clients can reset the + language. Note that servers implementing [RFC2518] might have + made this a protected property as this is a new requirement. + + COPY/MOVE behavior: This property value SHOULD be preserved in COPY + and MOVE operations. + + Description: The DAV:getcontentlanguage property MUST be defined on + any DAV-compliant resource that returns the Content-Language + header on a GET. + + + +15.4. getcontentlength Property + + Name: getcontentlength + + Purpose: Contains the Content-Length header returned by a GET + without accept headers. + + Value: See Section 14.13 of [RFC2616]. + + Protected: This property is computed, therefore protected. + + Description: The DAV:getcontentlength property MUST be defined on + any DAV-compliant resource that returns the Content-Length header + in response to a GET. + + COPY/MOVE behavior: This property value is dependent on the size of + the destination resource, not the value of the property on the + source resource. + + + +15.5. getcontenttype Property + + Name: getcontenttype + + Purpose: Contains the Content-Type header value (from Section 14.17 + of [RFC2616]) as it would be returned by a GET without accept + headers. + + Value: media-type (defined in Section 3.7 of [RFC2616]) + + Protected: Potentially protected if the server prefers to assign + content types on its own (see also discussion in Section 9.7.1). + + + + + +Dusseault Standards Track [Page 92] + +RFC 4918 WebDAV June 2007 + + + COPY/MOVE behavior: This property value SHOULD be preserved in COPY + and MOVE operations. + + Description: This property MUST be defined on any DAV-compliant + resource that returns the Content-Type header in response to a + GET. + + + +15.6. getetag Property + + Name: getetag + + Purpose: Contains the ETag header value (from Section 14.19 of + [RFC2616]) as it would be returned by a GET without accept + headers. + + Value: entity-tag (defined in Section 3.11 of [RFC2616]) + + Protected: MUST be protected because this value is created and + controlled by the server. + + COPY/MOVE behavior: This property value is dependent on the final + state of the destination resource, not the value of the property + on the source resource. Also note the considerations in + Section 8.8. + + Description: The getetag property MUST be defined on any DAV- + compliant resource that returns the Etag header. Refer to Section + 3.11 of RFC 2616 for a complete definition of the semantics of an + ETag, and to Section 8.6 for a discussion of ETags in WebDAV. + + + +15.7. getlastmodified Property + + Name: getlastmodified + + Purpose: Contains the Last-Modified header value (from Section + 14.29 of [RFC2616]) as it would be returned by a GET method + without accept headers. + + Value: rfc1123-date (defined in Section 3.3.1 of [RFC2616]) + + Protected: SHOULD be protected because some clients may rely on the + value for appropriate caching behavior, or on the value of the + Last-Modified header to which this property is linked. + + + + +Dusseault Standards Track [Page 93] + +RFC 4918 WebDAV June 2007 + + + COPY/MOVE behavior: This property value is dependent on the last + modified date of the destination resource, not the value of the + property on the source resource. Note that some server + implementations use the file system date modified value for the + DAV:getlastmodified value, and this can be preserved in a MOVE + even when the HTTP Last-Modified value SHOULD change. Note that + since [RFC2616] requires clients to use ETags where provided, a + server implementing ETags can count on clients using a much better + mechanism than modification dates for offline synchronization or + cache control. Also note the considerations in Section 8.8. + + Description: The last-modified date on a resource SHOULD only + reflect changes in the body (the GET responses) of the resource. + A change in a property only SHOULD NOT cause the last-modified + date to change, because clients MAY rely on the last-modified date + to know when to overwrite the existing body. The DAV: + getlastmodified property MUST be defined on any DAV-compliant + resource that returns the Last-Modified header in response to a + GET. + + + +15.8. lockdiscovery Property + + Name: lockdiscovery + + Purpose: Describes the active locks on a resource + + Protected: MUST be protected. Clients change the list of locks + through LOCK and UNLOCK, not through PROPPATCH. + + COPY/MOVE behavior: The value of this property depends on the lock + state of the destination, not on the locks of the source resource. + Recall that locks are not moved in a MOVE operation. + + Description: Returns a listing of who has a lock, what type of lock + he has, the timeout type and the time remaining on the timeout, + and the associated lock token. Owner information MAY be omitted + if it is considered sensitive. If there are no locks, but the + server supports locks, the property will be present but contain + zero 'activelock' elements. If there are one or more locks, an + 'activelock' element appears for each lock on the resource. This + property is NOT lockable with respect to write locks (Section 7). + + + + + + + + +Dusseault Standards Track [Page 94] + +RFC 4918 WebDAV June 2007 + + +15.8.1. Example - Retrieving DAV:lockdiscovery + + >>Request + + PROPFIND /container/ HTTP/1.1 + Host: www.example.com + Content-Length: xxxx + Content-Type: application/xml; charset="utf-8" + + + + + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.example.com/container/ + + + + + + + 0 + Jane Smith + Infinite + + urn:uuid:f81de2ad-7f3d-a1b2-4f3c-00a0c91a9d76 + + + http://www.example.com/container/ + + + + + HTTP/1.1 200 OK + + + + + + + +Dusseault Standards Track [Page 95] + +RFC 4918 WebDAV June 2007 + + + This resource has a single exclusive write lock on it, with an + infinite timeout. + +15.9. resourcetype Property + + Name: resourcetype + + Purpose: Specifies the nature of the resource. + + Protected: SHOULD be protected. Resource type is generally decided + through the operation creating the resource (MKCOL vs PUT), not by + PROPPATCH. + + COPY/MOVE behavior: Generally a COPY/MOVE of a resource results in + the same type of resource at the destination. + + Description: MUST be defined on all DAV-compliant resources. Each + child element identifies a specific type the resource belongs to, + such as 'collection', which is the only resource type defined by + this specification (see Section 14.3). If the element contains + the 'collection' child element plus additional unrecognized + elements, it should generally be treated as a collection. If the + element contains no recognized child elements, it should be + treated as a non-collection resource. The default value is empty. + This element MUST NOT contain text or mixed content. Any custom + child element is considered to be an identifier for a resource + type. + + Example: (fictional example to show extensibility) + + + + + + +15.10. supportedlock Property + + Name: supportedlock + + Purpose: To provide a listing of the lock capabilities supported by + the resource. + + Protected: MUST be protected. Servers, not clients, determine what + lock mechanisms are supported. + + + + + + + +Dusseault Standards Track [Page 96] + +RFC 4918 WebDAV June 2007 + + + COPY/MOVE behavior: This property value is dependent on the kind of + locks supported at the destination, not on the value of the + property at the source resource. Servers attempting to COPY to a + destination should not attempt to set this property at the + destination. + + Description: Returns a listing of the combinations of scope and + access types that may be specified in a lock request on the + resource. Note that the actual contents are themselves controlled + by access controls, so a server is not required to provide + information the client is not authorized to see. This property is + NOT lockable with respect to write locks (Section 7). + + + +15.10.1. Example - Retrieving DAV:supportedlock + + >>Request + + PROPFIND /container/ HTTP/1.1 + Host: www.example.com + Content-Length: xxxx + Content-Type: application/xml; charset="utf-8" + + + + + + + >>Response + + HTTP/1.1 207 Multi-Status + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + http://www.example.com/container/ + + + + + + + + + + + + +Dusseault Standards Track [Page 97] + +RFC 4918 WebDAV June 2007 + + + + + + + HTTP/1.1 200 OK + + + + +16. Precondition/Postcondition XML Elements + + As introduced in Section 8.7, extra information on error conditions + can be included in the body of many status responses. This section + makes requirements on the use of the error body mechanism and + introduces a number of precondition and postcondition codes. + + A "precondition" of a method describes the state of the server that + must be true for that method to be performed. A "postcondition" of a + method describes the state of the server that must be true after that + method has been completed. + + Each precondition and postcondition has a unique XML element + associated with it. In a 207 Multi-Status response, the XML element + MUST appear inside an 'error' element in the appropriate 'propstat or + 'response' element depending on whether the condition applies to one + or more properties or to the resource as a whole. In all other error + responses where this specification's 'error' body is used, the + precondition/postcondition XML element MUST be returned as the child + of a top-level 'error' element in the response body, unless otherwise + negotiated by the request, along with an appropriate response status. + The most common response status codes are 403 (Forbidden) if the + request should not be repeated because it will always fail, and 409 + (Conflict) if it is expected that the user might be able to resolve + the conflict and resubmit the request. The 'error' element MAY + contain child elements with specific error information and MAY be + extended with any custom child elements. + + This mechanism does not take the place of using a correct numeric + status code as defined here or in HTTP, because the client must + always be able to take a reasonable course of action based only on + the numeric code. However, it does remove the need to define new + numeric codes. The new machine-readable codes used for this purpose + are XML elements classified as preconditions and postconditions, so + naturally, any group defining a new condition code can use their own + namespace. As always, the "DAV:" namespace is reserved for use by + IETF-chartered WebDAV working groups. + + + + + +Dusseault Standards Track [Page 98] + +RFC 4918 WebDAV June 2007 + + + A server supporting this specification SHOULD use the XML error + whenever a precondition or postcondition defined in this document is + violated. For error conditions not specified in this document, the + server MAY simply choose an appropriate numeric status and leave the + response body blank. However, a server MAY instead use a custom + condition code and other supporting text, because even when clients + do not automatically recognize condition codes, they can be quite + useful in interoperability testing and debugging. + + Example - Response with precondition code + + >>Response + + HTTP/1.1 423 Locked + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + /workspace/webdav/ + + + + In this example, a client unaware of a depth-infinity lock on the + parent collection "/workspace/webdav/" attempted to modify the + collection member "/workspace/webdav/proposal.doc". + + Some other useful preconditions and postconditions have been defined + in other specifications extending WebDAV, such as [RFC3744] (see + particularly Section 7.1.1), [RFC3253], and [RFC3648]. + + All these elements are in the "DAV:" namespace. If not specified + otherwise, the content for each condition's XML element is defined to + be empty. + + + Name: lock-token-matches-request-uri + + Use with: 409 Conflict + + Purpose: (precondition) -- A request may include a Lock-Token header + to identify a lock for the UNLOCK method. However, if the + Request-URI does not fall within the scope of the lock identified + by the token, the server SHOULD use this error. The lock may have + a scope that does not include the Request-URI, or the lock could + have disappeared, or the token may be invalid. + + + + +Dusseault Standards Track [Page 99] + +RFC 4918 WebDAV June 2007 + + + Name: lock-token-submitted (precondition) + + Use with: 423 Locked + + Purpose: The request could not succeed because a lock token should + have been submitted. This element, if present, MUST contain at + least one URL of a locked resource that prevented the request. In + cases of MOVE, COPY, and DELETE where collection locks are + involved, it can be difficult for the client to find out which + locked resource made the request fail -- but the server is only + responsible for returning one such locked resource. The server + MAY return every locked resource that prevented the request from + succeeding if it knows them all. + + + + + Name: no-conflicting-lock (precondition) + + Use with: Typically 423 Locked + + Purpose: A LOCK request failed due the presence of an already + existing conflicting lock. Note that a lock can be in conflict + although the resource to which the request was directed is only + indirectly locked. In this case, the precondition code can be + used to inform the client about the resource that is the root of + the conflicting lock, avoiding a separate lookup of the + "lockdiscovery" property. + + + + + Name: no-external-entities + + Use with: 403 Forbidden + + Purpose: (precondition) -- If the server rejects a client request + because the request body contains an external entity, the server + SHOULD use this error. + + + Name: preserved-live-properties + + Use with: 409 Conflict + + Purpose: (postcondition) -- The server received an otherwise-valid + MOVE or COPY request, but cannot maintain the live properties with + the same behavior at the destination. It may be that the server + + + +Dusseault Standards Track [Page 100] + +RFC 4918 WebDAV June 2007 + + + only supports some live properties in some parts of the + repository, or simply has an internal error. + + + Name: propfind-finite-depth + + Use with: 403 Forbidden + + Purpose: (precondition) -- This server does not allow infinite-depth + PROPFIND requests on collections. + + + Name: cannot-modify-protected-property + + Use with: 403 Forbidden + + Purpose: (precondition) -- The client attempted to set a protected + property in a PROPPATCH (such as DAV:getetag). See also + [RFC3253], Section 3.12. + +17. XML Extensibility in DAV + + The XML namespace extension ([REC-XML-NAMES]) is used in this + specification in order to allow for new XML elements to be added + without fear of colliding with other element names. Although WebDAV + request and response bodies can be extended by arbitrary XML + elements, which can be ignored by the message recipient, an XML + element in the "DAV:" namespace SHOULD NOT be used in the request or + response body unless that XML element is explicitly defined in an + IETF RFC reviewed by a WebDAV working group. + + For WebDAV to be both extensible and backwards-compatible, both + clients and servers need to know how to behave when unexpected or + unrecognized command extensions are received. For XML processing, + this means that clients and servers MUST process received XML + documents as if unexpected elements and attributes (and all children + of unrecognized elements) were not there. An unexpected element or + attribute includes one that may be used in another context but is not + expected here. Ignoring such items for purposes of processing can of + course be consistent with logging all information or presenting for + debugging. + + This restriction also applies to the processing, by clients, of DAV + property values where unexpected XML elements SHOULD be ignored + unless the property's schema declares otherwise. + + This restriction does not apply to setting dead DAV properties on the + server where the server MUST record all XML elements. + + + +Dusseault Standards Track [Page 101] + +RFC 4918 WebDAV June 2007 + + + Additionally, this restriction does not apply to the use of XML where + XML happens to be the content type of the entity body, for example, + when used as the body of a PUT. + + Processing instructions in XML SHOULD be ignored by recipients. + Thus, specifications extending WebDAV SHOULD NOT use processing + instructions to define normative behavior. + + XML DTD fragments are included for all the XML elements defined in + this specification. However, correct XML will not be valid according + to any DTD due to namespace usage and extension rules. In + particular: + + o Elements (from this specification) are in the "DAV:" namespace, + + o Element ordering is irrelevant unless otherwise stated, + + o Extension attributes MAY be added, + + o For element type definitions of "ANY", the normative text + definition for that element defines what can be in it and what + that means. + + o For element type definitions of "#PCDATA", extension elements MUST + NOT be added. + + o For other element type definitions, including "EMPTY", extension + elements MAY be added. + + Note that this means that elements containing elements cannot be + extended to contain text, and vice versa. + + With DTD validation relaxed by the rules above, the constraints + described by the DTD fragments are normative (see for example + Appendix A). A recipient of a WebDAV message with an XML body MUST + NOT validate the XML document according to any hard-coded or + dynamically-declared DTD. + + Note that this section describes backwards-compatible extensibility + rules. There might also be times when an extension is designed not + to be backwards-compatible, for example, defining an extension that + reuses an XML element defined in this document but omitting one of + the child elements required by the DTDs in this specification. + + + + + + + + +Dusseault Standards Track [Page 102] + +RFC 4918 WebDAV June 2007 + + +18. DAV Compliance Classes + + A DAV-compliant resource can advertise several classes of compliance. + A client can discover the compliance classes of a resource by + executing OPTIONS on the resource and examining the "DAV" header + which is returned. Note particularly that resources, rather than + servers, are spoken of as being compliant. That is because + theoretically some resources on a server could support different + feature sets. For example, a server could have a sub-repository + where an advanced feature like versioning was supported, even if that + feature was not supported on all sub-repositories. + + Since this document describes extensions to the HTTP/1.1 protocol, + minimally all DAV-compliant resources, clients, and proxies MUST be + compliant with [RFC2616]. + + A resource that is class 2 or class 3 compliant must also be class 1 + compliant. + +18.1. Class 1 + + A class 1 compliant resource MUST meet all "MUST" requirements in all + sections of this document. + + Class 1 compliant resources MUST return, at minimum, the value "1" in + the DAV header on all responses to the OPTIONS method. + +18.2. Class 2 + + A class 2 compliant resource MUST meet all class 1 requirements and + support the LOCK method, the DAV:supportedlock property, the DAV: + lockdiscovery property, the Time-Out response header and the Lock- + Token request header. A class 2 compliant resource SHOULD also + support the Timeout request header and the 'owner' XML element. + + Class 2 compliant resources MUST return, at minimum, the values "1" + and "2" in the DAV header on all responses to the OPTIONS method. + +18.3. Class 3 + + A resource can explicitly advertise its support for the revisions to + [RFC2518] made in this document. Class 1 MUST be supported as well. + Class 2 MAY be supported. Advertising class 3 support in addition to + class 1 and 2 means that the server supports all the requirements in + this specification. Advertising class 3 and class 1 support, but not + class 2, means that the server supports all the requirements in this + specification except possibly those that involve locking support. + + + + +Dusseault Standards Track [Page 103] + +RFC 4918 WebDAV June 2007 + + + Example: + + DAV: 1, 3 + +19. Internationalization Considerations + + In the realm of internationalization, this specification complies + with the IETF Character Set Policy [RFC2277]. In this specification, + human-readable fields can be found either in the value of a property, + or in an error message returned in a response entity body. In both + cases, the human-readable content is encoded using XML, which has + explicit provisions for character set tagging and encoding, and + requires that XML processors read XML elements encoded, at minimum, + using the UTF-8 [RFC3629] and UTF-16 [RFC2781] encodings of the ISO + 10646 multilingual plane. XML examples in this specification + demonstrate use of the charset parameter of the Content-Type header + (defined in [RFC3023]), as well as XML charset declarations. + + XML also provides a language tagging capability for specifying the + language of the contents of a particular XML element. The "xml:lang" + attribute appears on an XML element to identify the language of its + content and attributes. See [REC-XML] for definitions of values and + scoping. + + WebDAV applications MUST support the character set tagging, character + set encoding, and the language tagging functionality of the XML + specification. Implementors of WebDAV applications are strongly + encouraged to read "XML Media Types" [RFC3023] for instruction on + which MIME media type to use for XML transport, and on use of the + charset parameter of the Content-Type header. + + Names used within this specification fall into four categories: names + of protocol elements such as methods and headers, names of XML + elements, names of properties, and names of conditions. Naming of + protocol elements follows the precedent of HTTP, using English names + encoded in US-ASCII for methods and headers. Since these protocol + elements are not visible to users, and are simply long token + identifiers, they do not need to support multiple languages. + Similarly, the names of XML elements used in this specification are + not visible to the user and hence do not need to support multiple + languages. + + WebDAV property names are qualified XML names (pairs of XML namespace + name and local name). Although some applications (e.g., a generic + property viewer) will display property names directly to their users, + it is expected that the typical application will use a fixed set of + properties, and will provide a mapping from the property name and + namespace to a human-readable field when displaying the property name + + + +Dusseault Standards Track [Page 104] + +RFC 4918 WebDAV June 2007 + + + to a user. It is only in the case where the set of properties is not + known ahead of time that an application need display a property name + to a user. We recommend that applications provide human-readable + property names wherever feasible. + + For error reporting, we follow the convention of HTTP/1.1 status + codes, including with each status code a short, English description + of the code (e.g., 423 (Locked)). While the possibility exists that + a poorly crafted user agent would display this message to a user, + internationalized applications will ignore this message, and display + an appropriate message in the user's language and character set. + + Since interoperation of clients and servers does not require locale + information, this specification does not specify any mechanism for + transmission of this information. + +20. Security Considerations + + This section is provided to detail issues concerning security + implications of which WebDAV applications need to be aware. + + All of the security considerations of HTTP/1.1 (discussed in + [RFC2616]) and XML (discussed in [RFC3023]) also apply to WebDAV. In + addition, the security risks inherent in remote authoring require + stronger authentication technology, introduce several new privacy + concerns, and may increase the hazards from poor server design. + These issues are detailed below. + +20.1. Authentication of Clients + + Due to their emphasis on authoring, WebDAV servers need to use + authentication technology to protect not just access to a network + resource, but the integrity of the resource as well. Furthermore, + the introduction of locking functionality requires support for + authentication. + + A password sent in the clear over an insecure channel is an + inadequate means for protecting the accessibility and integrity of a + resource as the password may be intercepted. Since Basic + authentication for HTTP/1.1 performs essentially clear text + transmission of a password, Basic authentication MUST NOT be used to + authenticate a WebDAV client to a server unless the connection is + secure. Furthermore, a WebDAV server MUST NOT send a Basic + authentication challenge in a WWW-Authenticate header unless the + connection is secure. An example of a secure connection would be a + Transport Layer Security (TLS) connection employing a strong cipher + suite and server authentication. + + + + +Dusseault Standards Track [Page 105] + +RFC 4918 WebDAV June 2007 + + + WebDAV applications MUST support the Digest authentication scheme + [RFC2617]. Since Digest authentication verifies that both parties to + a communication know a shared secret, a password, without having to + send that secret in the clear, Digest authentication avoids the + security problems inherent in Basic authentication while providing a + level of authentication that is useful in a wide range of scenarios. + +20.2. Denial of Service + + Denial-of-service attacks are of special concern to WebDAV servers. + WebDAV plus HTTP enables denial-of-service attacks on every part of a + system's resources. + + o The underlying storage can be attacked by PUTting extremely large + files. + + o Asking for recursive operations on large collections can attack + processing time. + + o Making multiple pipelined requests on multiple connections can + attack network connections. + + WebDAV servers need to be aware of the possibility of a denial-of- + service attack at all levels. The proper response to such an attack + MAY be to simply drop the connection. Or, if the server is able to + make a response, the server MAY use a 400-level status request such + as 400 (Bad Request) and indicate why the request was refused (a 500- + level status response would indicate that the problem is with the + server, whereas unintentional DoS attacks are something the client is + capable of remedying). + +20.3. Security through Obscurity + + WebDAV provides, through the PROPFIND method, a mechanism for listing + the member resources of a collection. This greatly diminishes the + effectiveness of security or privacy techniques that rely only on the + difficulty of discovering the names of network resources. Users of + WebDAV servers are encouraged to use access control techniques to + prevent unwanted access to resources, rather than depending on the + relative obscurity of their resource names. + +20.4. Privacy Issues Connected to Locks + + When submitting a lock request, a user agent may also submit an + 'owner' XML field giving contact information for the person taking + out the lock (for those cases where a person, rather than a robot, is + taking out the lock). This contact information is stored in a DAV: + lockdiscovery property on the resource, and can be used by other + + + +Dusseault Standards Track [Page 106] + +RFC 4918 WebDAV June 2007 + + + collaborators to begin negotiation over access to the resource. + However, in many cases, this contact information can be very private, + and should not be widely disseminated. Servers SHOULD limit read + access to the DAV:lockdiscovery property as appropriate. + Furthermore, user agents SHOULD provide control over whether contact + information is sent at all, and if contact information is sent, + control over exactly what information is sent. + +20.5. Privacy Issues Connected to Properties + + Since property values are typically used to hold information such as + the author of a document, there is the possibility that privacy + concerns could arise stemming from widespread access to a resource's + property data. To reduce the risk of inadvertent release of private + information via properties, servers are encouraged to develop access + control mechanisms that separate read access to the resource body and + read access to the resource's properties. This allows a user to + control the dissemination of their property data without overly + restricting access to the resource's contents. + +20.6. Implications of XML Entities + + XML supports a facility known as "external entities", defined in + Section 4.2.2 of [REC-XML], which instructs an XML processor to + retrieve and include additional XML. An external XML entity can be + used to append or modify the document type declaration (DTD) + associated with an XML document. An external XML entity can also be + used to include XML within the content of an XML document. For non- + validating XML, such as the XML used in this specification, including + an external XML entity is not required by XML. However, XML does + state that an XML processor may, at its discretion, include the + external XML entity. + + External XML entities have no inherent trustworthiness and are + subject to all the attacks that are endemic to any HTTP GET request. + Furthermore, it is possible for an external XML entity to modify the + DTD, and hence affect the final form of an XML document, in the worst + case, significantly modifying its semantics or exposing the XML + processor to the security risks discussed in [RFC3023]. Therefore, + implementers must be aware that external XML entities should be + treated as untrustworthy. If a server chooses not to handle external + XML entities, it SHOULD respond to requests containing external + entities with the 'no-external-entities' condition code. + + There is also the scalability risk that would accompany a widely + deployed application that made use of external XML entities. In this + situation, it is possible that there would be significant numbers of + requests for one external XML entity, potentially overloading any + + + +Dusseault Standards Track [Page 107] + +RFC 4918 WebDAV June 2007 + + + server that fields requests for the resource containing the external + XML entity. + + Furthermore, there's also a risk based on the evaluation of "internal + entities" as defined in Section 4.2.2 of [REC-XML]. A small, + carefully crafted request using nested internal entities may require + enormous amounts of memory and/or processing time to process. Server + implementers should be aware of this risk and configure their XML + parsers so that requests like these can be detected and rejected as + early as possible. + +20.7. Risks Connected with Lock Tokens + + This specification encourages the use of "A Universally Unique + Identifier (UUID) URN Namespace" ([RFC4122]) for lock tokens + (Section 6.5), in order to guarantee their uniqueness across space + and time. Version 1 UUIDs (defined in Section 4) MAY contain a + "node" field that "consists of an IEEE 802 MAC address, usually the + host address. For systems with multiple IEEE addresses, any + available one can be used". Since a WebDAV server will issue many + locks over its lifetime, the implication is that it may also be + publicly exposing its IEEE 802 address. + + There are several risks associated with exposure of IEEE 802 + addresses. Using the IEEE 802 address: + + o It is possible to track the movement of hardware from subnet to + subnet. + + o It may be possible to identify the manufacturer of the hardware + running a WebDAV server. + + o It may be possible to determine the number of each type of + computer running WebDAV. + + This risk only applies to host-address-based UUID versions. Section + 4 of [RFC4122] describes several other mechanisms for generating + UUIDs that do not involve the host address and therefore do not + suffer from this risk. + +20.8. Hosting Malicious Content + + HTTP has the ability to host programs that are executed on client + machines. These programs can take many forms including Web scripts, + executables, plug-in modules, and macros in documents. WebDAV does + not change any of the security concerns around these programs, yet + often WebDAV is used in contexts where a wide range of users can + publish documents on a server. The server might not have a close + + + +Dusseault Standards Track [Page 108] + +RFC 4918 WebDAV June 2007 + + + trust relationship with the author that is publishing the document. + Servers that allow clients to publish arbitrary content can usefully + implement precautions to check that content published to the server + is not harmful to other clients. Servers could do this by techniques + such as restricting the types of content that is allowed to be + published and running virus and malware detection software on + published content. Servers can also mitigate the risk by having + appropriate access restriction and authentication of users that are + allowed to publish content to the server. + +21. IANA Considerations + +21.1. New URI Schemes + + This specification defines two URI schemes: + + 1. the "opaquelocktoken" scheme defined in Appendix C, and + + 2. the "DAV" URI scheme, which historically was used in [RFC2518] to + disambiguate WebDAV property and XML element names and which + continues to be used for that purpose in this specification and + others extending WebDAV. Creation of identifiers in the "DAV:" + namespace is controlled by the IETF. + + Note that defining new URI schemes for XML namespaces is now + discouraged. "DAV:" was defined before standard best practices + emerged. + +21.2. XML Namespaces + + XML namespaces disambiguate WebDAV property names and XML elements. + Any WebDAV user or application can define a new namespace in order to + create custom properties or extend WebDAV XML syntax. IANA does not + need to manage such namespaces, property names, or element names. + +21.3. Message Header Fields + + The message header fields below should be added to the permanent + registry (see [RFC3864]). + +21.3.1. DAV + + Header field name: DAV + + Applicable protocol: http + + Status: standard + + + + +Dusseault Standards Track [Page 109] + +RFC 4918 WebDAV June 2007 + + + Author/Change controller: IETF + + Specification document: this specification (Section 10.1) + +21.3.2. Depth + + Header field name: Depth + + Applicable protocol: http + + Status: standard + + Author/Change controller: IETF + + Specification document: this specification (Section 10.2) + +21.3.3. Destination + + Header field name: Destination + + Applicable protocol: http + + Status: standard + + Author/Change controller: IETF + + Specification document: this specification (Section 10.3) + +21.3.4. If + + Header field name: If + + Applicable protocol: http + + Status: standard + + Author/Change controller: IETF + + Specification document: this specification (Section 10.4) + +21.3.5. Lock-Token + + Header field name: Lock-Token + + Applicable protocol: http + + Status: standard + + + + +Dusseault Standards Track [Page 110] + +RFC 4918 WebDAV June 2007 + + + Author/Change controller: IETF + + Specification document: this specification (Section 10.5) + +21.3.6. Overwrite + + Header field name: Overwrite + + Applicable protocol: http + + Status: standard + + Author/Change controller: IETF + + Specification document: this specification (Section 10.6) + +21.3.7. Timeout + + Header field name: Timeout + + Applicable protocol: http + + Status: standard + + Author/Change controller: IETF + + Specification document: this specification (Section 10.7) + +21.4. HTTP Status Codes + + This specification defines the HTTP status codes + + o 207 Multi-Status (Section 11.1) + + o 422 Unprocessable Entity (Section 11.2), + + o 423 Locked (Section 11.3), + + o 424 Failed Dependency (Section 11.4) and + + o 507 Insufficient Storage (Section 11.5), + + to be updated in the registry at + . + + Note: the HTTP status code 102 (Processing) has been removed in this + specification; its IANA registration should continue to reference RFC + 2518. + + + +Dusseault Standards Track [Page 111] + +RFC 4918 WebDAV June 2007 + + +22. Acknowledgements + + A specification such as this thrives on piercing critical review and + withers from apathetic neglect. The authors gratefully acknowledge + the contributions of the following people, whose insights were so + valuable at every stage of our work. + + Contributors to RFC 2518 + + Terry Allen, Harald Alvestrand, Jim Amsden, Becky Anderson, Alan + Babich, Sanford Barr, Dylan Barrell, Bernard Chester, Tim Berners- + Lee, Dan Connolly, Jim Cunningham, Ron Daniel, Jr., Jim Davis, Keith + Dawson, Mark Day, Brian Deen, Martin Duerst, David Durand, Lee + Farrell, Chuck Fay, Wesley Felter, Roy Fielding, Mark Fisher, Alan + Freier, George Florentine, Jim Gettys, Phill Hallam-Baker, Dennis + Hamilton, Steve Henning, Mead Himelstein, Alex Hopmann, Andre van der + Hoek, Ben Laurie, Paul Leach, Ora Lassila, Karen MacArthur, Steven + Martin, Larry Masinter, Michael Mealling, Keith Moore, Thomas Narten, + Henrik Nielsen, Kenji Ota, Bob Parker, Glenn Peterson, Jon Radoff, + Saveen Reddy, Henry Sanders, Christopher Seiwald, Judith Slein, Mike + Spreitzer, Einar Stefferud, Greg Stein, Ralph Swick, Kenji Takahashi, + Richard N. Taylor, Robert Thau, John Turner, Sankar Virdhagriswaran, + Fabio Vitali, Gregory Woodhouse, and Lauren Wood. + + Two from this list deserve special mention. The contributions by + Larry Masinter have been invaluable; he both helped the formation of + the working group and patiently coached the authors along the way. + In so many ways he has set high standards that we have toiled to + meet. The contributions of Judith Slein were also invaluable; by + clarifying the requirements and in patiently reviewing version after + version, she both improved this specification and expanded our minds + on document management. + + We would also like to thank John Turner for developing the XML DTD. + + The authors of RFC 2518 were Yaron Goland, Jim Whitehead, A. Faizi, + Steve Carter, and D. Jensen. Although their names had to be removed + due to IETF author count restrictions, they can take credit for the + majority of the design of WebDAV. + + Additional Acknowledgements for This Specification + + Significant contributors of text for this specification are listed as + contributors in the section below. We must also gratefully + acknowledge Geoff Clemm, Joel Soderberg, and Dan Brotsky for hashing + out specific text on the list or in meetings. Joe Hildebrand and + Cullen Jennings helped close many issues. Barry Lind described an + additional security consideration and Cullen Jennings provided text + + + +Dusseault Standards Track [Page 112] + +RFC 4918 WebDAV June 2007 + + + for that consideration. Jason Crawford tracked issue status for this + document for a period of years, followed by Elias Sinderson. + +23. Contributors to This Specification + + Julian Reschke + bytes GmbH + Hafenweg 16, 48155 Muenster, Germany + EMail: julian.reschke@greenbytes.de + + + Elias Sinderson + University of California, Santa Cruz + 1156 High Street, Santa Cruz, CA 95064 + EMail: elias@cse.ucsc.edu + + + Jim Whitehead + University of California, Santa Cruz + 1156 High Street, Santa Cruz, CA 95064 + EMail: ejw@soe.ucsc.edu + +24. Authors of RFC 2518 + + Y. Y. Goland + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052-6399 + EMail: yarong@microsoft.com + + + E. J. Whitehead, Jr. + Dept. Of Information and Computer Science + University of California, Irvine + Irvine, CA 92697-3425 + EMail: ejw@ics.uci.edu + + + A. Faizi + Netscape + 685 East Middlefield Road + Mountain View, CA 94043 + EMail: asad@netscape.com + + + + + + + + +Dusseault Standards Track [Page 113] + +RFC 4918 WebDAV June 2007 + + + S. R. Carter + Novell + 1555 N. Technology Way + M/S ORM F111 + Orem, UT 84097-2399 + EMail: srcarter@novell.com + + + D. Jensen + Novell + 1555 N. Technology Way + M/S ORM F111 + Orem, UT 84097-2399 + EMail: dcjensen@novell.com + +25. References + +25.1. Normative References + + [REC-XML] Bray, T., Paoli, J., Sperberg-McQueen, C., Maler, + E., and F. Yergeau, "Extensible Markup Language + (XML) 1.0 (Fourth Edition)", W3C REC-xml-20060816, + August 2006, + . + + [REC-XML-INFOSET] Cowan, J. and R. Tobin, "XML Information Set + (Second Edition)", W3C REC-xml-infoset-20040204, + February 2004, . + + [REC-XML-NAMES] Bray, T., Hollander, D., Layman, A., and R. Tobin, + "Namespaces in XML 1.0 (Second Edition)", W3C REC- + xml-names-20060816, August 2006, . + + [RFC2119] Bradner, S., "Key words for use in RFCs to + Indicate Requirement Levels", BCP 14, RFC 2119, + March 1997. + + [RFC2277] Alvestrand, H., "IETF Policy on Character Sets and + Languages", BCP 18, RFC 2277, January 1998. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, + "Hypertext Transfer Protocol -- HTTP/1.1", + RFC 2616, June 1999. + + + + + +Dusseault Standards Track [Page 114] + +RFC 4918 WebDAV June 2007 + + + [RFC2617] Franks, J., Hallam-Baker, P., Hostetler, J., + Lawrence, S., Leach, P., Luotonen, A., and L. + Stewart, "HTTP Authentication: Basic and Digest + Access Authentication", RFC 2617, June 1999. + + [RFC3339] Klyne, G., Ed. and C. Newman, "Date and Time on + the Internet: Timestamps", RFC 3339, July 2002. + + [RFC3629] Yergeau, F., "UTF-8, a transformation format of + ISO 10646", STD 63, RFC 3629, November 2003. + + [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, + "Uniform Resource Identifier (URI): Generic + Syntax", STD 66, RFC 3986, January 2005. + + [RFC4122] Leach, P., Mealling, M., and R. Salz, "A + Universally Unique IDentifier (UUID) URN + Namespace", RFC 4122, July 2005. + +25.2. Informative References + + [RFC2291] Slein, J., Vitali, F., Whitehead, E., and D. + Durand, "Requirements for a Distributed Authoring + and Versioning Protocol for the World Wide Web", + RFC 2291, February 1998. + + [RFC2518] Goland, Y., Whitehead, E., Faizi, A., Carter, S., + and D. Jensen, "HTTP Extensions for Distributed + Authoring -- WEBDAV", RFC 2518, February 1999. + + [RFC2781] Hoffman, P. and F. Yergeau, "UTF-16, an encoding + of ISO 10646", RFC 2781, February 2000. + + [RFC3023] Murata, M., St. Laurent, S., and D. Kohn, "XML + Media Types", RFC 3023, January 2001. + + [RFC3253] Clemm, G., Amsden, J., Ellison, T., Kaler, C., and + J. Whitehead, "Versioning Extensions to WebDAV + (Web Distributed Authoring and Versioning)", + RFC 3253, March 2002. + + [RFC3648] Whitehead, J. and J. Reschke, Ed., "Web + Distributed Authoring and Versioning (WebDAV) + Ordered Collections Protocol", RFC 3648, + December 2003. + + + + + + +Dusseault Standards Track [Page 115] + +RFC 4918 WebDAV June 2007 + + + [RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. + Whitehead, "Web Distributed Authoring and + Versioning (WebDAV) Access Control Protocol", + RFC 3744, May 2004. + + [RFC3864] Klyne, G., Nottingham, M., and J. Mogul, + "Registration Procedures for Message Header + Fields", BCP 90, RFC 3864, September 2004. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Dusseault Standards Track [Page 116] + +RFC 4918 WebDAV June 2007 + + +Appendix A. Notes on Processing XML Elements + +A.1. Notes on Empty XML Elements + + XML supports two mechanisms for indicating that an XML element does + not have any content. The first is to declare an XML element of the + form . The second is to declare an XML element of the form + . The two XML elements are semantically identical. + +A.2. Notes on Illegal XML Processing + + XML is a flexible data format that makes it easy to submit data that + appears legal but in fact is not. The philosophy of "Be flexible in + what you accept and strict in what you send" still applies, but it + must not be applied inappropriately. XML is extremely flexible in + dealing with issues of whitespace, element ordering, inserting new + elements, etc. This flexibility does not require extension, + especially not in the area of the meaning of elements. + + There is no kindness in accepting illegal combinations of XML + elements. At best, it will cause an unwanted result and at worst it + can cause real damage. + +A.3. Example - XML Syntax Error + + The following request body for a PROPFIND method is illegal. + + + + + + + + The definition of the propfind element only allows for the allprop or + the propname element, not both. Thus, the above is an error and must + be responded to with a 400 (Bad Request). + + Imagine, however, that a server wanted to be "kind" and decided to + pick the allprop element as the true element and respond to it. A + client running over a bandwidth limited line who intended to execute + a propname would be in for a big surprise if the server treated the + command as an allprop. + + Additionally, if a server were lenient and decided to reply to this + request, the results would vary randomly from server to server, with + some servers executing the allprop directive, and others executing + the propname directive. This reduces interoperability rather than + increasing it. + + + +Dusseault Standards Track [Page 117] + +RFC 4918 WebDAV June 2007 + + +A.4. Example - Unexpected XML Element + + The previous example was illegal because it contained two elements + that were explicitly banned from appearing together in the propfind + element. However, XML is an extensible language, so one can imagine + new elements being defined for use with propfind. Below is the + request body of a PROPFIND and, like the previous example, must be + rejected with a 400 (Bad Request) by a server that does not + understand the expired-props element. + + + + + + + To understand why a 400 (Bad Request) is returned, let us look at the + request body as the server unfamiliar with expired-props sees it. + + + + + + As the server does not understand the 'expired-props' element, + according to the WebDAV-specific XML processing rules specified in + Section 17, it must process the request as if the element were not + there. Thus, the server sees an empty propfind, which by the + definition of the propfind element is illegal. + + Please note that had the extension been additive, it would not + necessarily have resulted in a 400 (Bad Request). For example, + imagine the following request body for a PROPFIND: + + + + + + *boss* + + + The previous example contains the fictitious element leave-out. Its + purpose is to prevent the return of any property whose name matches + the submitted pattern. If the previous example were submitted to a + server unfamiliar with 'leave-out', the only result would be that the + 'leave-out' element would be ignored and a propname would be + executed. + + + +Dusseault Standards Track [Page 118] + +RFC 4918 WebDAV June 2007 + + +Appendix B. Notes on HTTP Client Compatibility + + WebDAV was designed to be, and has been found to be, backward- + compatible with HTTP 1.1. The PUT and DELETE methods are defined in + HTTP and thus may be used by HTTP clients as well as WebDAV-aware + clients, but the responses to PUT and DELETE have been extended in + this specification in ways that only a WebDAV client would be + entirely prepared for. Some theoretical concerns were raised about + whether those responses would cause interoperability problems with + HTTP-only clients, and this section addresses those concerns. + + Since any HTTP client ought to handle unrecognized 400-level and 500- + level status codes as errors, the following new status codes should + not present any issues: 422, 423, and 507 (424 is also a new status + code but it appears only in the body of a Multistatus response.) So, + for example, if an HTTP client attempted to PUT or DELETE a locked + resource, the 423 Locked response ought to result in a generic error + presented to the user. + + The 207 Multistatus response is interesting because an HTTP client + issuing a DELETE request to a collection might interpret a 207 + response as a success, even though it does not realize the resource + is a collection and cannot understand that the DELETE operation might + have been a complete or partial failure. That interpretation isn't + entirely justified, because a 200-level response indicates that the + server "received, understood, and accepted" the request, not that the + request resulted in complete success. + + One option is that a server could treat a DELETE of a collection as + an atomic operation, and use either 204 No Content in case of + success, or some appropriate error response (400 or 500 level) for an + error. This approach would indeed maximize backward compatibility. + However, since interoperability tests and working group discussions + have not turned up any instances of HTTP clients issuing a DELETE + request against a WebDAV collection, this concern is more theoretical + than practical. Thus, servers are likely to be completely successful + at interoperating with HTTP clients even if they treat any collection + DELETE request as a WebDAV request and send a 207 Multi-Status + response. + + In general, server implementations are encouraged to use the detailed + responses and other mechanisms defined in this document rather than + make changes for theoretical interoperability concerns. + + + + + + + + +Dusseault Standards Track [Page 119] + +RFC 4918 WebDAV June 2007 + + +Appendix C. The 'opaquelocktoken' Scheme and URIs + + The 'opaquelocktoken' URI scheme was defined in [RFC2518] (and + registered by IANA) in order to create syntactically correct and + easy-to-generate URIs out of UUIDs, intended to be used as lock + tokens and to be unique across all resources for all time. + + An opaquelocktoken URI is constructed by concatenating the + 'opaquelocktoken' scheme with a UUID, along with an optional + extension. Servers can create new UUIDs for each new lock token. If + a server wishes to reuse UUIDs, the server MUST add an extension, and + the algorithm generating the extension MUST guarantee that the same + extension will never be used twice with the associated UUID. + + OpaqueLockToken-URI = "opaquelocktoken:" UUID [Extension] + ; UUID is defined in Section 3 of [RFC4122]. Note that LWS + ; is not allowed between elements of + ; this production. + + Extension = path + ; path is defined in Section 3.3 of [RFC3986] + + +Appendix D. Lock-null Resources + + The original WebDAV model for locking unmapped URLs created "lock- + null resources". This model was over-complicated and some + interoperability and implementation problems were discovered. The + new WebDAV model for locking unmapped URLs (see Section 7.3) creates + "locked empty resources". Lock-null resources are deprecated. This + section discusses the original model briefly because clients MUST be + able to handle either model. + + In the original "lock-null resource" model, which is no longer + recommended for implementation: + + o A lock-null resource sometimes appeared as "Not Found". The + server responds with a 404 or 405 to any method except for PUT, + MKCOL, OPTIONS, PROPFIND, LOCK, UNLOCK. + + o A lock-null resource does however show up as a member of its + parent collection. + + o The server removes the lock-null resource entirely (its URI + becomes unmapped) if its lock goes away before it is converted to + a regular resource. Recall that locks go away not only when they + expire or are unlocked, but are also removed if a resource is + renamed or moved, or if any parent collection is renamed or moved. + + + +Dusseault Standards Track [Page 120] + +RFC 4918 WebDAV June 2007 + + + o The server converts the lock-null resource into a regular resource + if a PUT request to the URL is successful. + + o The server converts the lock-null resource into a collection if a + MKCOL request to the URL is successful (though interoperability + experience showed that not all servers followed this requirement). + + o Property values were defined for DAV:lockdiscovery and DAV: + supportedlock properties but not necessarily for other properties + like DAV:getcontenttype. + + Clients can easily interoperate both with servers that support the + old model "lock-null resources" and the recommended model of "locked + empty resources" by only attempting PUT after a LOCK to an unmapped + URL, not MKCOL or GET. + +D.1. Guidance for Clients Using LOCK to Create Resources + + A WebDAV client implemented to this specification might find servers + that create lock-null resources (implemented before this + specification using [RFC2518]) as well as servers that create locked + empty resources. The response to the LOCK request will not indicate + what kind of resource was created. There are a few techniques that + help the client deal with either type. + + If the client wishes to avoid accidentally creating either lock- + null or empty locked resources, an "If-Match: *" header can be + included with LOCK requests to prevent the server from creating a + new resource. + + If a LOCK request creates a resource and the client subsequently + wants to overwrite that resource using a COPY or MOVE request, the + client should include an "Overwrite: T" header. + + If a LOCK request creates a resource and the client then decides + to get rid of that resource, a DELETE request is supposed to fail + on a lock-null resource and UNLOCK should be used instead. But + with a locked empty resource, UNLOCK doesn't make the resource + disappear. Therefore, the client might have to try both requests + and ignore an error in one of the two requests. + +Appendix E. Guidance for Clients Desiring to Authenticate + + Many WebDAV clients that have already been implemented have account + settings (similar to the way email clients store IMAP account + settings). Thus, the WebDAV client would be able to authenticate + with its first couple requests to the server, provided it had a way + to get the authentication challenge from the server with realm name, + + + +Dusseault Standards Track [Page 121] + +RFC 4918 WebDAV June 2007 + + + nonce, and other challenge information. Note that the results of + some requests might vary according to whether or not the client is + authenticated -- a PROPFIND might return more visible resources if + the client is authenticated, yet not fail if the client is anonymous. + + There are a number of ways the client might be able to trigger the + server to provide an authentication challenge. This appendix + describes a couple approaches that seem particularly likely to work. + + The first approach is to perform a request that ought to require + authentication. However, it's possible that a server might handle + any request even without authentication, so to be entirely safe, the + client could add a conditional header to ensure that even if the + request passes permissions checks, it's not actually handled by the + server. An example of following this approach would be to use a PUT + request with an "If-Match" header with a made-up ETag value. This + approach might fail to result in an authentication challenge if the + server does not test authorization before testing conditionals as is + required (see Section 8.5), or if the server does not need to test + authorization. + + Example - forcing auth challenge with write request + + >>Request + + PUT /forceauth.txt HTTP/1.1 + Host: www.example.com + If-Match: "xxx" + Content-Type: text/plain + Content-Length: 0 + + + The second approach is to use an Authorization header (defined in + [RFC2617]), which is likely to be rejected by the server but which + will then prompt a proper authentication challenge. For example, the + client could start with a PROPFIND request containing an + Authorization header containing a made-up Basic userid:password + string or with actual plausible credentials. This approach relies on + the server responding with a "401 Unauthorized" along with a + challenge if it receives an Authorization header with an unrecognized + username, invalid password, or if it doesn't even handle Basic + authentication. This seems likely to work because of the + requirements of RFC 2617: + + + + + + + + +Dusseault Standards Track [Page 122] + +RFC 4918 WebDAV June 2007 + + + "If the origin server does not wish to accept the credentials sent + with a request, it SHOULD return a 401 (Unauthorized) response. The + response MUST include a WWW-Authenticate header field containing at + least one (possibly new) challenge applicable to the requested + resource." + + There's a slight problem with implementing that recommendation in + some cases, because some servers do not even have challenge + information for certain resources. Thus, when there's no way to + authenticate to a resource or the resource is entirely publicly + available over all accepted methods, the server MAY ignore the + Authorization header, and the client will presumably try again later. + + Example - forcing auth challenge with Authorization header + + >>Request + + PROPFIND /docs/ HTTP/1.1 + Host: www.example.com + Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== + Content-type: application/xml; charset="utf-8" + Content-Length: xxxx + + [body omitted] + + +Appendix F. Summary of Changes from RFC 2518 + + This section lists major changes between this document and RFC 2518, + starting with those that are likely to result in implementation + changes. Servers will advertise support for all changes in this + specification by returning the compliance class "3" in the DAV + response header (see Sections 10.1 and 18.3). + +F.1. Changes for Both Client and Server Implementations + + Collections and Namespace Operations + + o The semantics of PROPFIND 'allprop' (Section 9.1) have been + relaxed so that servers return results including, at a minimum, + the live properties defined in this specification, but not + necessarily return other live properties. The 'allprop' directive + therefore means something more like "return all properties that + are supposed to be returned when 'allprop' is requested" -- a set + of properties that may include custom properties and properties + defined in other specifications if those other specifications so + require. Related to this, 'allprop' requests can now be extended + with the 'include' syntax to include specific named properties, + + + +Dusseault Standards Track [Page 123] + +RFC 4918 WebDAV June 2007 + + + thereby avoiding additional requests due to changed 'allprop' + semantics. + + o Servers are now allowed to reject PROPFIND requests with Depth: + Infinity. Clients that used this will need to be able to do a + series of Depth:1 requests instead. + + o Multi-Status response bodies now can transport the value of HTTP's + Location response header in the new 'location' element. Clients + may use this to avoid additional roundtrips to the server when + there is a 'response' element with a 3xx status (see + Section 14.24). + + o The definition of COPY has been relaxed so that it doesn't require + servers to first delete the target resources anymore (this was a + known incompatibility with [RFC3253]). See Section 9.8. + + Headers and Marshalling + + o The Destination and If request headers now allow absolute paths in + addition to full URIs (see Section 8.3). This may be useful for + clients operating through a reverse proxy that does rewrite the + Host request header, but not WebDAV-specific headers. + + o This specification adopts the error marshalling extensions and the + "precondition/postcondition" terminology defined in [RFC3253] (see + Section 16). Related to that, it adds the "error" XML element + inside multistatus response bodies (see Section 14.5, however note + that it uses a format different from the one recommended in RFC + 3253). + + o Senders and recipients are now required to support the UTF-16 + character encoding in XML message bodies (see Section 19). + + o Clients are now required to send the Depth header on PROPFIND + requests, although servers are still encouraged to support clients + that don't. + + Locking + + o RFC 2518's concept of "lock-null resources" (LNRs) has been + replaced by a simplified approach, the "locked empty resources" + (see Section 7.3). There are some aspects of lock-null resources + clients cannot rely on anymore, namely, the ability to use them to + create a locked collection or the fact that they disappear upon + UNLOCK when no PUT or MKCOL request was issued. Note that servers + are still allowed to implement LNRs as per RFC 2518. + + + + +Dusseault Standards Track [Page 124] + +RFC 4918 WebDAV June 2007 + + + o There is no implicit refresh of locks anymore. Locks are only + refreshed upon explicit request (see Section 9.10.2). + + o Clarified that the DAV:owner value supplied in the LOCK request + must be preserved by the server just like a dead property + (Section 14.17). Also added the DAV:lockroot element + (Section 14.12), which allows clients to discover the root of + lock. + +F.2. Changes for Server Implementations + + Collections and Namespace Operations + + o Due to interoperability problems, allowable formats for contents + of 'href' elements in multistatus responses have been limited (see + Section 8.3). + + o Due to lack of implementation, support for the 'propertybehavior' + request body for COPY and MOVE has been removed. Instead, + requirements for property preservation have been clarified (see + Sections 9.8 and 9.9). + + Properties + + o Strengthened server requirements for storage of property values, + in particular persistence of language information (xml:lang), + whitespace, and XML namespace information (see Section 4.3). + + o Clarified requirements on which properties should be writable by + the client; in particular, setting "DAV:displayname" should be + supported by servers (see Section 15). + + o Only 'rfc1123-date' productions are legal as values for DAV: + getlastmodified (see Section 15.7). + + Headers and Marshalling + + o Servers are now required to do authorization checks before + processing conditional headers (see Section 8.5). + + Locking + + o Strengthened requirement to check identity of lock creator when + accessing locked resources (see Section 6.4). Clients should be + aware that lock tokens returned to other principals can only be + used to break a lock, if at all. + + + + + +Dusseault Standards Track [Page 125] + +RFC 4918 WebDAV June 2007 + + + o Section 8.10.4 of [RFC2518] incorrectly required servers to return + a 409 status where a 207 status was really appropriate. This has + been corrected (Section 9.10). + +F.3. Other Changes + + The definition of collection state has been fixed so it doesn't vary + anymore depending on the Request-URI (see Section 5.2). + + The DAV:source property introduced in Section 4.6 of [RFC2518] was + removed due to lack of implementation experience. + + The DAV header now allows non-IETF extensions through URIs in + addition to compliance class tokens. It also can now be used in + requests, although this specification does not define any associated + semantics for the compliance classes defined in here (see + Section 10.1). + + In RFC 2518, the definition of the Depth header (Section 9.2) + required that, by default, request headers would be applied to each + resource in scope. Based on implementation experience, the default + has now been reversed (see Section 10.2). + + The definitions of HTTP status code 102 ([RFC2518], Section 10.1) and + the Status-URI response header (Section 9.7) have been removed due to + lack of implementation. + + The TimeType format used in the Timeout request header and the + "timeout" XML element used to be extensible. Now, only the two + formats defined by this specification are allowed (see Section 10.7). + +Author's Address + + Lisa Dusseault (editor) + CommerceNet + 2064 Edgewood Dr. + Palo Alto, CA 94303 + US + + EMail: ldusseault@commerce.net + + + + + + + + + + + +Dusseault Standards Track [Page 126] + +RFC 4918 WebDAV June 2007 + + +Full Copyright Statement + + Copyright (C) The IETF Trust (2007). + + This document is subject to the rights, licenses and restrictions + contained in BCP 78, and except as set forth therein, the authors + retain all their rights. + + This document and the information contained herein are provided on an + "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS + OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND + THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF + THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Intellectual Property + + The IETF takes no position regarding the validity or scope of any + Intellectual Property Rights or other rights that might be claimed to + pertain to the implementation or use of the technology described in + this document or the extent to which any license under such rights + might or might not be available; nor does it represent that it has + made any independent effort to identify any such rights. Information + on the procedures with respect to rights in RFC documents can be + found in BCP 78 and BCP 79. + + Copies of IPR disclosures made to the IETF Secretariat and any + assurances of licenses to be made available, or the result of an + attempt made to obtain a general license or permission for the use of + such proprietary rights by implementers or users of this + specification can be obtained from the IETF on-line IPR repository at + http://www.ietf.org/ipr. + + The IETF invites any interested party to bring to its attention any + copyrights, patents or patent applications, or other proprietary + rights that may cover technology that may be required to implement + this standard. Please address the information to the IETF at + ietf-ipr@ietf.org. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + +Dusseault Standards Track [Page 127] + diff --git a/doc/rfc5397-webdav-current-principal-extension.txt b/doc/rfc5397-webdav-current-principal-extension.txt new file mode 100644 index 00000000..616055e7 --- /dev/null +++ b/doc/rfc5397-webdav-current-principal-extension.txt @@ -0,0 +1,281 @@ + + + +Network Working Group W. Sanchez +Request for Comments: 5397 C. Daboo +Category: Standards Track Apple Inc. + December 2008 + + + WebDAV Current Principal Extension + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (c) 2008 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. + +Abstract + + This specification defines a new WebDAV property that allows clients + to quickly determine the principal corresponding to the current + authenticated user. + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2 + 2. Conventions Used in This Document . . . . . . . . . . . . . . . 2 + 3. DAV:current-user-principal . . . . . . . . . . . . . . . . . . 3 + 4. Security Considerations . . . . . . . . . . . . . . . . . . . . 4 + 5. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . 4 + 6. Normative References . . . . . . . . . . . . . . . . . . . . . 4 + + + + + + + + + +Sanchez & Daboo Standards Track [Page 1] + +RFC 5397 WebDAV Current Principal December 2008 + + +1. Introduction + + WebDAV [RFC4918] is an extension to HTTP [RFC2616] to support + improved document authoring capabilities. The WebDAV Access Control + Protocol ("WebDAV ACL") [RFC3744] extension adds access control + capabilities to WebDAV. It introduces the concept of a "principal" + resource, which is used to represent information about authenticated + entities on the system. + + Some clients have a need to determine which [RFC3744] principal a + server is associating with the currently authenticated HTTP user. + While [RFC3744] defines a DAV:current-user-privilege-set property for + retrieving the privileges granted to that principal, there is no + recommended way to identify the principal in question, which is + necessary to perform other useful operations. For example, a client + may wish to determine which groups the current user is a member of, + or modify a property of the principal resource associated with the + current user. + + The DAV:principal-match REPORT provides some useful functionality, + but there are common situations where the results from that query can + be ambiguous. For example, not only is an individual user principal + returned, but also every group principal that the user is a member + of, and there is no clear way to distinguish which is which. + + This specification proposes an extension to WebDAV ACL that adds a + DAV:current-user-principal property to resources under access control + on the server. This property provides a URL to a principal resource + corresponding to the currently authenticated user. This allows a + client to "bootstrap" itself by performing additional queries on the + principal resource to obtain additional information from that + resource, which is the purpose of this extension. Note that while it + is possible for multiple URLs to refer to the same principal + resource, or for multiple principal resources to correspond to a + single principal, this specification only allows for a single http(s) + URL in the DAV:current-user-principal property. If a client wishes + to obtain alternate URLs for the principal, it can query the + principal resource for this information; it is not the purpose of + this extension to provide a complete list of such URLs, but simply to + provide a means to locate a resource which contains that (and other) + information. + +2. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + + + +Sanchez & Daboo Standards Track [Page 2] + +RFC 5397 WebDAV Current Principal December 2008 + + + When XML element types in the namespace "DAV:" are referenced in this + document outside of the context of an XML fragment, the string "DAV:" + will be prefixed to the element type names. + + Processing of XML by clients and servers MUST follow the rules + defined in Section 17 of WebDAV [RFC4918]. + + Some of the declarations refer to XML elements defined by WebDAV + [RFC4918]. + +3. DAV:current-user-principal + + Name: current-user-principal + + Namespace: DAV: + + Purpose: Indicates a URL for the currently authenticated user's + principal resource on the server. + + Value: A single DAV:href or DAV:unauthenticated element. + + Protected: This property is computed on a per-request basis, and + therefore is protected. + + Description: The DAV:current-user-principal property contains either + a DAV:href or DAV:unauthenticated XML element. The DAV:href + element contains a URL to a principal resource corresponding to + the currently authenticated user. That URL MUST be one of the + URLs in the DAV:principal-URL or DAV:alternate-URI-set properties + defined on the principal resource and MUST be an http(s) scheme + URL. When authentication has not been done or has failed, this + property MUST contain the DAV:unauthenticated pseudo-principal. + + In some cases, there may be multiple principal resources + corresponding to the same authenticated principal. In that case, + the server is free to choose any one of the principal resource + URIs for the value of the DAV:current-user-principal property. + However, servers SHOULD be consistent and use the same principal + resource URI for each authenticated principal. + + COPY/MOVE behavior: This property is computed on a per-request + basis, and is thus never copied or moved. + + Definition: + + + + + + + +Sanchez & Daboo Standards Track [Page 3] + +RFC 5397 WebDAV Current Principal December 2008 + + + Example: + + + /principals/users/cdaboo + + +4. Security Considerations + + This specification does not introduce any additional security issues + beyond those defined for HTTP [RFC2616], WebDAV [RFC4918], and WebDAV + ACL [RFC3744]. + +5. Acknowledgments + + This specification is based on discussions that took place within the + Calendaring and Scheduling Consortium's CalDAV Technical Committee. + The authors thank the participants of that group for their input. + + The authors thank Julian Reschke for his valuable input via the + WebDAV working group mailing list. + +6. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + + [RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. Whitehead, "Web + Distributed Authoring and Versioning (WebDAV) + Access Control Protocol", RFC 3744, May 2004. + + [RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed + Authoring and Versioning (WebDAV)", RFC 4918, June 2007. + +Authors' Addresses + + Wilfredo Sanchez + Apple Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + EMail: wsanchez@wsanchez.net + URI: http://www.apple.com/ + + + + +Sanchez & Daboo Standards Track [Page 4] + +RFC 5397 WebDAV Current Principal December 2008 + + + Cyrus Daboo + Apple Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + EMail: cyrus@daboo.name + URI: http://www.apple.com/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Sanchez & Daboo Standards Track [Page 5] + + diff --git a/doc/rfc5785-well-known-uris.txt b/doc/rfc5785-well-known-uris.txt new file mode 100644 index 00000000..c28ccf6b --- /dev/null +++ b/doc/rfc5785-well-known-uris.txt @@ -0,0 +1,451 @@ + + + + + + +Internet Engineering Task Force (IETF) M. Nottingham +Request for Comments: 5785 E. Hammer-Lahav +Updates: 2616, 2818 April 2010 +Category: Standards Track +ISSN: 2070-1721 + + + Defining Well-Known Uniform Resource Identifiers (URIs) + +Abstract + + This memo defines a path prefix for "well-known locations", + "/.well-known/", in selected Uniform Resource Identifier (URI) + schemes. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc5785. + +Copyright Notice + + Copyright (c) 2010 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + + + + + + + +Nottingham & Hammer-Lahav Standards Track [Page 1] + +RFC 5785 Defining Well-Known URIs April 2010 + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2 + 1.1. Appropriate Use of Well-Known URIs . . . . . . . . . . . . 3 + 2. Notational Conventions . . . . . . . . . . . . . . . . . . . . 3 + 3. Well-Known URIs . . . . . . . . . . . . . . . . . . . . . . . . 3 + 4. Security Considerations . . . . . . . . . . . . . . . . . . . . 4 + 5. IANA Considerations . . . . . . . . . . . . . . . . . . . . . . 4 + 5.1. The Well-Known URI Registry . . . . . . . . . . . . . . . . 4 + 5.1.1. Registration Template . . . . . . . . . . . . . . . . . 5 + 6. References . . . . . . . . . . . . . . . . . . . . . . . . . . 5 + 6.1. Normative References . . . . . . . . . . . . . . . . . . . 5 + 6.2. Informative References . . . . . . . . . . . . . . . . . . 5 + Appendix A. Acknowledgements . . . . . . . . . . . . . . . . . . . 7 + Appendix B. Frequently Asked Questions . . . . . . . . . . . . . . 7 + +1. Introduction + + It is increasingly common for Web-based protocols to require the + discovery of policy or other information about a host ("site-wide + metadata") before making a request. For example, the Robots + Exclusion Protocol specifies a way for + automated processes to obtain permission to access resources; + likewise, the Platform for Privacy Preferences [W3C.REC-P3P-20020416] + tells user-agents how to discover privacy policy beforehand. + + While there are several ways to access per-resource metadata (e.g., + HTTP headers, WebDAV's PROPFIND [RFC4918]), the perceived overhead + (either in terms of client-perceived latency and/or deployment + difficulties) associated with them often precludes their use in these + scenarios. + + When this happens, it is common to designate a "well-known location" + for such data, so that it can be easily located. However, this + approach has the drawback of risking collisions, both with other such + designated "well-known locations" and with pre-existing resources. + + To address this, this memo defines a path prefix in HTTP(S) URIs for + these "well-known locations", "/.well-known/". Future specifications + that need to define a resource for such site-wide metadata can + register their use to avoid collisions and minimise impingement upon + sites' URI space. + + + + + + + + + +Nottingham & Hammer-Lahav Standards Track [Page 2] + +RFC 5785 Defining Well-Known URIs April 2010 + + +1.1. Appropriate Use of Well-Known URIs + + There are a number of possible ways that applications could use Well- + known URIs. However, in keeping with the Architecture of the World- + Wide Web [W3C.REC-webarch-20041215], well-known URIs are not intended + for general information retrieval or establishment of large URI + namespaces on the Web. Rather, they are designed to facilitate + discovery of information on a site when it isn't practical to use + other mechanisms; for example, when discovering policy that needs to + be evaluated before a resource is accessed, or when using multiple + round-trips is judged detrimental to performance. + + As such, the well-known URI space was created with the expectation + that it will be used to make site-wide policy information and other + metadata available directly (if sufficiently concise), or provide + references to other URIs that provide such metadata. + +2. Notational Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in RFC 2119 [RFC2119]. + +3. Well-Known URIs + + A well-known URI is a URI [RFC3986] whose path component begins with + the characters "/.well-known/", and whose scheme is "HTTP", "HTTPS", + or another scheme that has explicitly been specified to use well- + known URIs. + + Applications that wish to mint new well-known URIs MUST register + them, following the procedures in Section 5.1. + + For example, if an application registers the name 'example', the + corresponding well-known URI on 'http://www.example.com/' would be + 'http://www.example.com/.well-known/example'. + + Registered names MUST conform to the segment-nz production in + [RFC3986]. + + Note that this specification defines neither how to determine the + authority to use for a particular context, nor the scope of the + metadata discovered by dereferencing the well-known URI; both should + be defined by the application itself. + + Typically, a registration will reference a specification that defines + the format and associated media type to be obtained by dereferencing + the well-known URI. + + + +Nottingham & Hammer-Lahav Standards Track [Page 3] + +RFC 5785 Defining Well-Known URIs April 2010 + + + It MAY also contain additional information, such as the syntax of + additional path components, query strings and/or fragment identifiers + to be appended to the well-known URI, or protocol-specific details + (e.g., HTTP [RFC2616] method handling). + + Note that this specification does not define a format or media-type + for the resource located at "/.well-known/" and clients should not + expect a resource to exist at that location. + +4. Security Considerations + + This memo does not specify the scope of applicability of metadata or + policy obtained from a well-known URI, and does not specify how to + discover a well-known URI for a particular application. Individual + applications using this mechanism must define both aspects. + + Applications minting new well-known URIs, as well as administrators + deploying them, will need to consider several security-related + issues, including (but not limited to) exposure of sensitive data, + denial-of-service attacks (in addition to normal load issues), server + and client authentication, vulnerability to DNS rebinding attacks, + and attacks where limited access to a server grants the ability to + affect how well-known URIs are served. + +5. IANA Considerations + +5.1. The Well-Known URI Registry + + This document establishes the well-known URI registry. + + Well-known URIs are registered on the advice of one or more + Designated Experts (appointed by the IESG or their delegate), with a + Specification Required (using terminology from [RFC5226]). However, + to allow for the allocation of values prior to publication, the + Designated Expert(s) may approve registration once they are satisfied + that such a specification will be published. + + Registration requests should be sent to the + wellknown-uri-review@ietf.org mailing list for review and comment, + with an appropriate subject (e.g., "Request for well-known URI: + example"). + + Before a period of 14 days has passed, the Designated Expert(s) will + either approve or deny the registration request, communicating this + decision both to the review list and to IANA. Denials should include + an explanation and, if applicable, suggestions as to how to make the + + + + + +Nottingham & Hammer-Lahav Standards Track [Page 4] + +RFC 5785 Defining Well-Known URIs April 2010 + + + request successful. Registration requests that are undetermined for + a period longer than 21 days can be brought to the IESG's attention + (using the iesg@iesg.org mailing list) for resolution. + +5.1.1. Registration Template + + URI suffix: The name requested for the well-known URI, relative to + "/.well-known/"; e.g., "example". + + Change controller: For Standards-Track RFCs, state "IETF". For + others, give the name of the responsible party. Other details + (e.g., postal address, e-mail address, home page URI) may also be + included. + + Specification document(s): Reference to the document that specifies + the field, preferably including a URI that can be used to retrieve + a copy of the document. An indication of the relevant sections + may also be included, but is not required. + + Related information: Optionally, citations to additional documents + containing further relevant information. + +6. References + +6.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform + Resource Identifier (URI): Generic Syntax", STD 66, + RFC 3986, January 2005. + + [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an + IANA Considerations Section in RFCs", BCP 26, RFC 5226, + May 2008. + +6.2. Informative References + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, + L., Leach, P., and T. Berners-Lee, "Hypertext Transfer + Protocol -- HTTP/1.1", RFC 2616, June 1999. + + [RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed + Authoring and Versioning (WebDAV)", RFC 4918, June 2007. + + + + + + +Nottingham & Hammer-Lahav Standards Track [Page 5] + +RFC 5785 Defining Well-Known URIs April 2010 + + + [W3C.REC-P3P-20020416] + Marchiori, M., "The Platform for Privacy Preferences 1.0 + (P3P1.0) Specification", World Wide Web Consortium + Recommendation REC-P3P-20020416, April 2002, + . + + [W3C.REC-webarch-20041215] + Jacobs, I. and N. Walsh, "Architecture of the World Wide + Web, Volume One", World Wide Web Consortium + Recommendation REC- webarch-20041215, December 2004, + . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Nottingham & Hammer-Lahav Standards Track [Page 6] + +RFC 5785 Defining Well-Known URIs April 2010 + + +Appendix A. Acknowledgements + + We would like to acknowledge the contributions of everyone who + provided feedback and use cases for this document; in particular, + Phil Archer, Dirk Balfanz, Adam Barth, Tim Bray, Brian Eaton, Brad + Fitzpatrick, Joe Gregorio, Paul Hoffman, Barry Leiba, Ashok Malhotra, + Breno de Medeiros, John Panzer, and Drummond Reed. However, they are + not responsible for errors and omissions. + +Appendix B. Frequently Asked Questions + + 1. Aren't well-known locations bad for the Web? + + They are, but for various reasons -- both technical and social -- + they are commonly used and their use is increasing. This memo + defines a "sandbox" for them, to reduce the risks of collision and + to minimise the impact upon pre-existing URIs on sites. + + 2. Why /.well-known? + + It's short, descriptive, and according to search indices, not + widely used. + + 3. What impact does this have on existing mechanisms, such as P3P and + robots.txt? + + None, until they choose to use this mechanism. + + 4. Why aren't per-directory well-known locations defined? + + Allowing every URI path segment to have a well-known location + (e.g., "/images/.well-known/") would increase the risks of + colliding with a pre-existing URI on a site, and generally these + solutions are found not to scale well, because they're too + "chatty". + + + + + + + + + + + + + + + + +Nottingham & Hammer-Lahav Standards Track [Page 7] + +RFC 5785 Defining Well-Known URIs April 2010 + + +Authors' Addresses + + Mark Nottingham + + EMail: mnot@mnot.net + URI: http://www.mnot.net/ + + + Eran Hammer-Lahav + + EMail: eran@hueniverse.com + URI: http://hueniverse.com/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Nottingham & Hammer-Lahav Standards Track [Page 8] + diff --git a/doc/rfc6352-carddav.txt b/doc/rfc6352-carddav.txt new file mode 100644 index 00000000..cb03747b --- /dev/null +++ b/doc/rfc6352-carddav.txt @@ -0,0 +1,2691 @@ + + + + + + +Internet Engineering Task Force (IETF) C. Daboo +Request for Comments: 6352 Apple +Category: Standards Track August 2011 +ISSN: 2070-1721 + + + CardDAV: vCard Extensions to + Web Distributed Authoring and Versioning (WebDAV) + +Abstract + + This document defines extensions to the Web Distributed Authoring and + Versioning (WebDAV) protocol to specify a standard way of accessing, + managing, and sharing contact information based on the vCard format. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc6352. + +Copyright Notice + + Copyright (c) 2011 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + + + +Daboo Standards Track [Page 1] + +RFC 6352 CardDAV August 2011 + + + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + +Table of Contents + + 1. Introduction and Overview . . . . . . . . . . . . . . . . . . 4 + 2. Conventions . . . . . . . . . . . . . . . . . . . . . . . . . 5 + 3. Requirements Overview . . . . . . . . . . . . . . . . . . . . 6 + 4. Address Book Data Model . . . . . . . . . . . . . . . . . . . 7 + 4.1. Address Book Server . . . . . . . . . . . . . . . . . . . 7 + 5. Address Book Resources . . . . . . . . . . . . . . . . . . . . 7 + 5.1. Address Object Resources . . . . . . . . . . . . . . . . . 7 + 5.1.1. Data Type Conversion . . . . . . . . . . . . . . . . . 8 + 5.1.1.1. Additional Precondition for GET . . . . . . . . . 8 + 5.2. Address Book Collections . . . . . . . . . . . . . . . . . 9 + 6. Address Book Feature . . . . . . . . . . . . . . . . . . . . . 10 + 6.1. Address Book Support . . . . . . . . . . . . . . . . . . . 10 + 6.1.1. Example: Using OPTIONS for the Discovery of + Support for CardDAV . . . . . . . . . . . . . . . . . 10 + 6.2. Address Book Properties . . . . . . . . . . . . . . . . . 10 + 6.2.1. CARDDAV:addressbook-description Property . . . . . . . 10 + 6.2.2. CARDDAV:supported-address-data Property . . . . . . . 11 + 6.2.3. CARDDAV:max-resource-size Property . . . . . . . . . . 12 + 6.3. Creating Resources . . . . . . . . . . . . . . . . . . . . 13 + 6.3.1. Extended MKCOL Method . . . . . . . . . . . . . . . . 13 + 6.3.1.1. Example - Successful MKCOL Request . . . . . . . . 14 + 6.3.2. Creating Address Object Resources . . . . . . . . . . 15 + 6.3.2.1. Additional Preconditions for PUT, COPY, and + MOVE . . . . . . . . . . . . . . . . . . . . . . . 16 + 6.3.2.2. Non-Standard vCard Properties and Parameters . . . 17 + 6.3.2.3. Address Object Resource Entity Tag . . . . . . . . 18 + 7. Address Book Access Control . . . . . . . . . . . . . . . . . 18 + 7.1. Additional Principal Properties . . . . . . . . . . . . . 18 + 7.1.1. CARDDAV:addressbook-home-set Property . . . . . . . . 19 + 7.1.2. CARDDAV:principal-address Property . . . . . . . . . . 19 + 8. Address Book Reports . . . . . . . . . . . . . . . . . . . . . 20 + 8.1. REPORT Method . . . . . . . . . . . . . . . . . . . . . . 20 + 8.2. Ordinary Collections . . . . . . . . . . . . . . . . . . . 21 + 8.3. Searching Text: Collations . . . . . . . . . . . . . . . . 21 + 8.3.1. CARDDAV:supported-collation-set Property . . . . . . . 22 + 8.4. Partial Retrieval . . . . . . . . . . . . . . . . . . . . 23 + 8.5. Non-Standard Properties and Parameters . . . . . . . . . . 23 + + + + +Daboo Standards Track [Page 2] + +RFC 6352 CardDAV August 2011 + + + 8.6. CARDDAV:addressbook-query Report . . . . . . . . . . . . . 23 + 8.6.1. Limiting Results . . . . . . . . . . . . . . . . . . . 25 + 8.6.2. Truncation of Results . . . . . . . . . . . . . . . . 25 + 8.6.3. Example: Partial Retrieval of vCards Matching + NICKNAME . . . . . . . . . . . . . . . . . . . . . . . 26 + 8.6.4. Example: Partial Retrieval of vCards Matching a + Full Name or Email Address . . . . . . . . . . . . . . 27 + 8.6.5. Example: Truncated Results . . . . . . . . . . . . . . 29 + 8.7. CARDDAV:addressbook-multiget Report . . . . . . . . . . . 31 + 8.7.1. Example: CARDDAV:addressbook-multiget Report . . . . . 32 + 8.7.2. Example: CARDDAV:addressbook-multiget Report . . . . . 33 + 9. Client Guidelines . . . . . . . . . . . . . . . . . . . . . . 34 + 9.1. Restrict the Properties Returned . . . . . . . . . . . . . 34 + 9.2. Avoiding Lost Updates . . . . . . . . . . . . . . . . . . 35 + 9.3. Client Configuration . . . . . . . . . . . . . . . . . . . 35 + 9.4. Finding Other Users' Address Books . . . . . . . . . . . . 35 + 10. XML Element Definitions . . . . . . . . . . . . . . . . . . . 36 + 10.1. CARDDAV:addressbook XML Element . . . . . . . . . . . . . 36 + 10.2. CARDDAV:supported-collation XML Element . . . . . . . . . 36 + 10.3. CARDDAV:addressbook-query XML Element . . . . . . . . . . 37 + 10.4. CARDDAV:address-data XML Element . . . . . . . . . . . . . 37 + 10.4.1. CARDDAV:allprop XML Element . . . . . . . . . . . . . 39 + 10.4.2. CARDDAV:prop XML Element . . . . . . . . . . . . . . . 39 + 10.5. CARDDAV:filter XML Element . . . . . . . . . . . . . . . . 40 + 10.5.1. CARDDAV:prop-filter XML Element . . . . . . . . . . . 40 + 10.5.2. CARDDAV:param-filter XML Element . . . . . . . . . . . 41 + 10.5.3. CARDDAV:is-not-defined XML Element . . . . . . . . . . 42 + 10.5.4. CARDDAV:text-match XML Element . . . . . . . . . . . . 42 + 10.6. CARDDAV:limit XML Element . . . . . . . . . . . . . . . . 43 + 10.6.1. CARDDAV:nresults XML Element . . . . . . . . . . . . . 44 + 10.7. CARDDAV:addressbook-multiget XML Element . . . . . . . . . 44 + 11. Service Discovery via SRV Records . . . . . . . . . . . . . . 45 + 12. Internationalization Considerations . . . . . . . . . . . . . 45 + 13. Security Considerations . . . . . . . . . . . . . . . . . . . 45 + 14. IANA Consideration . . . . . . . . . . . . . . . . . . . . . . 46 + 14.1. Namespace Registration . . . . . . . . . . . . . . . . . . 46 + 15. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 46 + 16. References . . . . . . . . . . . . . . . . . . . . . . . . . . 47 + 16.1. Normative References . . . . . . . . . . . . . . . . . . . 47 + 16.2. Informative References . . . . . . . . . . . . . . . . . . 48 + + + + + + + + + + + +Daboo Standards Track [Page 3] + +RFC 6352 CardDAV August 2011 + + +1. Introduction and Overview + + Address books containing contact information are a key component of + personal information management tools, such as email, calendaring and + scheduling, and instant messaging clients. To date several protocols + have been used for remote access to contact data, including the + Lightweight Directory Access Protocol (LDAP) [RFC4510], Internet + Message Support Protocol [IMSP], and Application Configuration Access + Protocol (ACAP) [RFC2244], together with SyncML used for + synchronization of such data. + + WebDAV [RFC4918] offers a number of advantages as a framework or + basis for address book access and management. Most of these + advantages boil down to a significant reduction in the costs of + design, implementation, interoperability testing, and deployment. + + The key features of address book support with WebDAV are: + + 1. Ability to use multiple address books with hierarchical layout. + + 2. Ability to control access to individual address books and address + entries as per WebDAV Access Control List (ACL) [RFC3744]. + + 3. Principal collections can be used to enumerate and query other + users on the system as per WebDAV ACL [RFC3744]. + + 4. Server-side searching of address data, avoiding the need for + clients to download an entire address book in order to do a quick + address 'expansion' operation. + + 5. Well-defined internationalization support through WebDAV's use of + XML. + + 6. Use of vCards [RFC2426] for well-defined address schema to + enhance client interoperability. + + 7. Many limited clients (e.g., mobile devices) contain an HTTP stack + that makes implementing WebDAV much easier than other protocols. + + The key disadvantage of address book support in WebDAV is: + + 1. Lack of change notification. Many of the alternative protocols + also lack this ability. However, an extension for push + notifications could easily be developed. + + vCard is a MIME directory profile aimed at encapsulating personal + addressing and contact information about people. The specification + of vCard was originally done by the Versit consortium, with a + + + +Daboo Standards Track [Page 4] + +RFC 6352 CardDAV August 2011 + + + subsequent 3.0 version standardized by the IETF [RFC2426]. vCard is + in widespread use in email clients and mobile devices as a means of + encapsulating address information for transport via email or for + import/export and synchronization operations. + + An update to vCard -- vCard v4 -- is currently being developed + [RFC6350] and is compatible with this specification. + +2. Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + The term "protected" is used in the Conformance field of property + definitions as defined in Section 15 of [RFC4918]. + + This document uses XML DTD fragments ([W3C.REC-xml-20081126], Section + 3.2) as a purely notational convention. WebDAV request and response + bodies cannot be validated by a DTD due to the specific extensibility + rules defined in Section 17 of [RFC4918] and due to the fact that all + XML elements defined by that specification use the XML namespace name + "DAV:". In particular: + + 1. Element names use the "DAV:" namespace. + + 2. Element ordering is irrelevant unless explicitly stated. + + 3. Extension elements (elements not already defined as valid child + elements) may be added anywhere, except when explicitly stated + otherwise. + + 4. Extension attributes (attributes not already defined as valid for + this element) may be added anywhere, except when explicitly + stated otherwise. + + The namespace "urn:ietf:params:xml:ns:carddav" is reserved for the + XML elements defined in this specification, its revisions, and + related CardDAV specifications. XML elements defined by individual + implementations MUST NOT use the "urn:ietf:params:xml:ns:carddav" + namespace, and instead should use a namespace that they control. + + When XML element types in the namespaces "DAV:" and + "urn:ietf:params:xml:ns:carddav" are referenced in this document + outside of the context of an XML fragment, the strings "DAV:" and + "CARDDAV:" will be prefixed to the element types, respectively. + + + + + +Daboo Standards Track [Page 5] + +RFC 6352 CardDAV August 2011 + + + This document inherits, and sometimes extends, DTD productions from + Section 14 of [RFC4918]. + + Also, note that some CardDAV XML element names are identical to + WebDAV XML element names, though their namespace differs. Care must + be taken not to confuse the two sets of names. + +3. Requirements Overview + + This section lists what functionality is required of a CardDAV + server. To advertise support for CardDAV, a server: + + o MUST support vCard v3 [RFC2426] as a media type for the address + object resource format; + + o MUST support WebDAV Class 3 [RFC4918]; + + o MUST support WebDAV ACL [RFC3744]; + + o MUST support secure transport as defined in [RFC2818] using + Transport Layer Security (TLS) [RFC5246] and using the certificate + validation procedures described in [RFC5280]; + + o MUST support ETags [RFC2616] with additional requirements + specified in Section 6.3.2.3 of this document; + + o MUST support all address book reports defined in Section 8 of this + document; and + + o MUST advertise support on all address book collections and address + object resources for the address book reports in the + DAV:supported-report-set property, as defined in Versioning + Extensions to WebDAV [RFC3253]. + + In addition, a server: + + o SHOULD support vCard v4 [RFC6350] as a media type for the address + object resource format; + + o SHOULD support the extended MKCOL method [RFC5689] to create + address book collections as defined in Section 6.3.1 of this + document. + + o SHOULD support the DAV:current-user-principal-URL property as + defined in [RFC5397] to give clients a fast way to locate user + principals. + + + + + +Daboo Standards Track [Page 6] + +RFC 6352 CardDAV August 2011 + + +4. Address Book Data Model + + As a brief overview, a CardDAV address book is modeled as a WebDAV + collection with a well-defined structure; each of these address book + collections contains a number of resources representing address + objects as their direct child resources. Each resource representing + an address object is called an "address object resource". Each + address object resource and each address book collection can be + individually locked and have individual WebDAV properties. + Requirements derived from this model are provided in Sections 5.1 and + 5.2. + +4.1. Address Book Server + + A CardDAV server is an address-aware engine combined with a WebDAV + server. The server may include address data in some parts of its URL + namespace and non-address data in other parts. + + A WebDAV server can advertise itself as a CardDAV server if it + supports the functionality defined in this specification at any point + within the root of its repository. That might mean that address data + is spread throughout the repository and mixed with non-address data + in nearby collections (e.g., address data may be found in /lisa/ + addressbook/ as well as in /bernard/addressbook/, and non-address + data in /lisa/calendars/). Or, it might mean that address data can + be found only in certain sections of the repository (e.g., + /addressbooks/user/). Address book features are only required in the + repository sections that are or contain address objects. So, a + repository confining address data to the /carddav/ collection would + only need to support the CardDAV required features within that + collection. + + The CardDAV server is the canonical location for address data and + state information. Clients may submit requests to change data or + download data. Clients may store address objects offline and attempt + to synchronize at a later time. Address data on the server can + change between the time of last synchronization and when attempting + an update, as address book collections may be shared and accessible + via multiple clients. Entity tags and locking help this work. + +5. Address Book Resources + +5.1. Address Object Resources + + This specification uses vCard as the default format for address or + contact information being stored on the server. However, this + specification does allow other formats for address data provided that + the server advertises support for those additional formats as + + + +Daboo Standards Track [Page 7] + +RFC 6352 CardDAV August 2011 + + + described below. The requirements in this section pertain to vCard + address data or formats that follow the semantics of vCard data. + + Address object resources contained in address book collections MUST + contain a single vCard component only. + + vCard components in an address book collection MUST have a UID + property value that MUST be unique in the scope of the address book + collection in which it is contained. + +5.1.1. Data Type Conversion + + Servers might support more than one primary media type for address + object resources, for example, vCard v3.0 and vCard v4.0. In such + cases, servers have to accept all media types that they advertise via + the CARDDAV:supported-address-data WebDAV property (see + Section 6.2.2). + + However, clients can use standard HTTP content negotiation behavior + (the Accept request header defined in Section 14.1 of [RFC2616]) to + request that an address object resource's data be returned in a + specific media type format. For example, a client merely capable of + handling vCard v3.0 would only want to have address object resources + returned in v3.0 format. + + Additionally, REPORT requests, defined later in this specification, + allow for the return of address object resource data within an XML + response body. Again, the client can use content negotiation to + request that data be returned in a specific media type by specifying + appropriate attributes on the CARDDAV:address-data XML element used + in the request body (see Section 10.4). + + In some cases, it might not be possible for a server to convert from + one media type to another. When that happens, the server MUST return + the CARDDAV:supported-address-data-conversion precondition (see + below) in the response body (when the failure to convert applies to + the entire response) or use that same precondition code in the + DAV:response XML element in the response for the targeted address + object resource when one of the REPORTs defined below is used. See + Section 8.7.2 for an example of this. + +5.1.1.1. Additional Precondition for GET + + This specification creates additional preconditions for the GET + method. + + + + + + +Daboo Standards Track [Page 8] + +RFC 6352 CardDAV August 2011 + + + The new precondition is: + + (CARDDAV:supported-address-data-conversion): The resource targeted + by the GET request can be converted to the media type specified in + the Accept request header included with the request. + +5.2. Address Book Collections + + Address book collections appear to clients as a WebDAV collection + resource, identified by a URL. An address book collection MUST + report the DAV:collection and CARDDAV:addressbook XML elements in the + value of the DAV:resourcetype property. The element type declaration + for CARDDAV:addressbook is: + + + + An address book collection can be created through provisioning (e.g., + automatically created when a user's account is provisioned), or it + can be created with the extended MKCOL method (see Section 6.3.1). + This can be used by a user to create additional address books (e.g., + "soccer team members") or for users to share an address book (e.g., + "sales team contacts"). However, note that this document doesn't + define what extra address book collections are for. Users must rely + on non-standard cues to find out what an address book collection is + for, or use the CARDDAV:addressbook-description property defined in + Section 6.2.1 to provide such a cue. + + The following restrictions are applied to the resources within an + address book collection: + + a. Address book collections MUST only contain address object + resources and collections that are not address book collections. + That is, the only "top-level" non-collection resources allowed in + an address book collection are address object resources. This + ensures that address book clients do not have to deal with non- + address data in an address book collection, though they do have + to distinguish between address object resources and collections + when using standard WebDAV techniques to examine the contents of + a collection. + + b. Collections contained in address book collections MUST NOT + contain address book collections at any depth. That is, + "nesting" of address book collections within other address book + collections at any depth is not allowed. This specification does + not define how collections contained in an address book + collection are used or how they relate to any address object + resources contained in the address book collection. + + + + +Daboo Standards Track [Page 9] + +RFC 6352 CardDAV August 2011 + + + Multiple address book collections MAY be children of the same + collection. + +6. Address Book Feature + +6.1. Address Book Support + + A server supporting the features described in this document MUST + include "addressbook" as a field in the DAV response header from an + OPTIONS request on any resource that supports any address book + properties, reports, or methods. A value of "addressbook" in the DAV + response header MUST indicate that the server supports all MUST level + requirements and REQUIRED features specified in this document. + +6.1.1. Example: Using OPTIONS for the Discovery of Support for CardDAV + + >> Request << + + OPTIONS /addressbooks/users/ HTTP/1.1 + Host: addressbook.example.com + + >> Response << + + HTTP/1.1 200 OK + Allow: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE + Allow: MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK, REPORT, ACL + DAV: 1, 2, 3, access-control, addressbook + DAV: extended-mkcol + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Length: 0 + + In this example, the OPTIONS response indicates that the server + supports CardDAV in this namespace; therefore, the '/addressbooks/ + users/' collection may be used as a parent for address book + collections as the extended MKCOL method is available and as a + possible target for REPORT requests for address book reports. + +6.2. Address Book Properties + +6.2.1. CARDDAV:addressbook-description Property + + Name: addressbook-description + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Provides a human-readable description of the address book + collection. + + + + +Daboo Standards Track [Page 10] + +RFC 6352 CardDAV August 2011 + + + Value: Any text. + + Protected: SHOULD NOT be protected so that users can specify a + description. + + COPY/MOVE behavior: This property value SHOULD be preserved in COPY + and MOVE operations. + + allprop behavior: SHOULD NOT be returned by a PROPFIND DAV:allprop + request. + + Description: This property contains a description of the address + book collection that is suitable for presentation to a user. The + xml:lang attribute can be used to add a language tag for the value + of this property. + + Definition: + + + + + Example: + + Adresses de Oliver Daboo + +6.2.2. CARDDAV:supported-address-data Property + + Name: supported-address-data + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies what media types are allowed for address object + resources in an address book collection. + + Protected: MUST be protected as it indicates the level of support + provided by the server. + + COPY/MOVE behavior: This property value MUST be preserved in COPY + and MOVE operations. + + allprop behavior: SHOULD NOT be returned by a PROPFIND DAV:allprop + request. + + Description: The CARDDAV:supported-address-data property is used to + specify the media type supported for the address object resources + contained in a given address book collection (e.g., vCard version + + + +Daboo Standards Track [Page 11] + +RFC 6352 CardDAV August 2011 + + + 3.0). Any attempt by the client to store address object resources + with a media type not listed in this property MUST result in an + error, with the CARDDAV:supported-address-data precondition + (Section 6.3.2.1) being violated. In the absence of this + property, the server MUST only accept data with the media type + "text/vcard" and vCard version 3.0, and clients can assume that is + all the server will accept. + + Definition: + + + + + + + + + Example: + + + + + +6.2.3. CARDDAV:max-resource-size Property + + Name: max-resource-size + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Provides a numeric value indicating the maximum size in + octets of a resource that the server is willing to accept when an + address object resource is stored in an address book collection. + + Value: Any text representing a numeric value. + + Protected: MUST be protected as it indicates limits provided by the + server. + + COPY/MOVE behavior: This property value MUST be preserved in COPY + and MOVE operations. + + allprop behavior: SHOULD NOT be returned by a PROPFIND DAV:allprop + request. + + + + + + +Daboo Standards Track [Page 12] + +RFC 6352 CardDAV August 2011 + + + Description: The CARDDAV:max-resource-size is used to specify a + numeric value that represents the maximum size in octets that the + server is willing to accept when an address object resource is + stored in an address book collection. Any attempt to store an + address book object resource exceeding this size MUST result in an + error, with the CARDDAV:max-resource-size precondition + (Section 6.3.2.1) being violated. In the absence of this + property, the client can assume that the server will allow storing + a resource of any reasonable size. + + Definition: + + + + + Example: + + 102400 + +6.3. Creating Resources + + Address book collections and address object resources may be created + by either a CardDAV client or the CardDAV server. This specification + defines restrictions and a data model that both clients and servers + MUST adhere to when manipulating such address data. + +6.3.1. Extended MKCOL Method + + An HTTP request using the extended MKCOL method [RFC5689] can be used + to create a new address book collection resource. A server MAY + restrict address book collection creation to particular collections. + + To create an address book, the client sends an extended MKCOL request + to the server and in the body of the request sets the + DAV:resourcetype property to the resource type for an address book + collection as defined in Section 5.2. + + Support for creating address books on the server is only RECOMMENDED + and not REQUIRED because some address book stores only support one + address book per user (or principal), and those are typically pre- + created for each account. However, servers and clients are strongly + encouraged to support address book creation whenever possible to + allow users to create multiple address book collections to help + organize their data better. + + + + + + +Daboo Standards Track [Page 13] + +RFC 6352 CardDAV August 2011 + + + The DAV:displayname property can be used for a human-readable name of + the address book. Clients can either specify the value of the + DAV:displayname property in the request body of the extended MKCOL + request or, alternatively, issue a PROPPATCH request to change the + DAV:displayname property to the appropriate value immediately after + using the extended MKCOL request. When displaying address book + collections to users, clients SHOULD check the DAV:displayname + property and use that value as the name of the address book. In the + event that the DAV:displayname property is not set, the client MAY + use the last part of the address book collection URI as the name; + however, that path segment may be "opaque" and not represent any + meaningful human-readable text. + +6.3.1.1. Example - Successful MKCOL Request + + This example creates an address book collection called /home/lisa/ + addressbook/ on the server addressbook.example.com with specific + values for the properties DAV:resourcetype, DAV:displayname, and + CARDDAV:addressbook-description. + + >> Request << + + MKCOL /home/lisa/addressbook/ HTTP/1.1 + Host: addressbook.example.com + Content-Type: text/xml; charset="utf-8" + Content-Length: xxx + + + + + + + + + + Lisa's Contacts + My primary address book. + + + + + + + + + + + + +Daboo Standards Track [Page 14] + +RFC 6352 CardDAV August 2011 + + + >> Response << + + HTTP/1.1 201 Created + Cache-Control: no-cache + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: application/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + HTTP/1.1 200 OK + + + +6.3.2. Creating Address Object Resources + + Clients populate address book collections with address object + resources. The URL for each address object resource is entirely + arbitrary and does not need to bear a specific relationship (but + might) to the address object resource's vCard properties or other + metadata. New address object resources MUST be created with a PUT + request targeted at an unmapped URI. A PUT request targeted at a + mapped URI updates an existing address object resource. + + When servers create new resources, it's not hard for the server to + choose a unique URL. It's slightly tougher for clients, because a + client might not want to examine all resources in the collection and + might not want to lock the entire collection to ensure that a new one + isn't created with a name collision. However, there is an HTTP + feature to mitigate this. If the client intends to create a new + address resource, the client SHOULD use the HTTP header "If-None- + Match: *" on the PUT request. The Request-URI on the PUT request + MUST include the target collection, where the resource is to be + created, plus the name of the resource in the last path segment. The + "If-None-Match" header ensures that the client will not inadvertently + overwrite an existing resource even if the last path segment turned + out to already be used. + + + + + + + +Daboo Standards Track [Page 15] + +RFC 6352 CardDAV August 2011 + + + >> Request << + + PUT /lisa/addressbook/newvcard.vcf HTTP/1.1 + If-None-Match: * + Host: addressbook.example.com + Content-Type: text/vcard + Content-Length: xxx + + BEGIN:VCARD + VERSION:3.0 + FN:Cyrus Daboo + N:Daboo;Cyrus + ADR;TYPE=POSTAL:;2822 Email HQ;Suite 2821;RFCVille;PA;15213;USA + EMAIL;TYPE=INTERNET,PREF:cyrus@example.com + NICKNAME:me + NOTE:Example VCard. + ORG:Self Employed + TEL;TYPE=WORK,VOICE:412 605 0499 + TEL;TYPE=FAX:412 605 0705 + URL:http://www.example.com + UID:1234-5678-9000-1 + END:VCARD + + >> Response << + + HTTP/1.1 201 Created + Date: Thu, 02 Sep 2004 16:53:32 GMT + Content-Length: 0 + ETag: "123456789-000-111" + + The request to change an existing address object resource without + overwriting a change made on the server uses a specific ETag in an + "If-Match" header, rather than the "If-None-Match" header. + + File names for vCards are commonly suffixed by ".vcf", and clients + may choose to use the same convention for URLs. + +6.3.2.1. Additional Preconditions for PUT, COPY, and MOVE + + This specification creates additional preconditions for the PUT, + COPY, and MOVE methods. These preconditions apply: + + o When a PUT operation of an address object resource into an address + book collection occurs. + + o When a COPY or MOVE operation of an address object resource into + an address book collection occurs. + + + + +Daboo Standards Track [Page 16] + +RFC 6352 CardDAV August 2011 + + + The new preconditions are: + + (CARDDAV:supported-address-data): The resource submitted in the + PUT request, or targeted by a COPY or MOVE request, MUST be a + supported media type (i.e., vCard) for address object resources. + + (CARDDAV:valid-address-data): The resource submitted in the PUT + request, or targeted by a COPY or MOVE request, MUST be valid data + for the media type being specified (i.e., MUST contain valid vCard + data). + + (CARDDAV:no-uid-conflict): The resource submitted in the PUT + request, or targeted by a COPY or MOVE request, MUST NOT specify a + vCard UID property value already in use in the targeted address + book collection or overwrite an existing address object resource + with one that has a different UID property value. Servers SHOULD + report the URL of the resource that is already making use of the + same UID property value in the DAV:href element. + + + + (CARDDAV:addressbook-collection-location-ok): In a COPY or MOVE + request, when the Request-URI is an address book collection, the + URI targeted by the Destination HTTP Request header MUST identify + a location where an address book collection can be created. + + (CARDDAV:max-resource-size): The resource submitted in the PUT + request, or targeted by a COPY or MOVE request, MUST have a size + in octets less than or equal to the value of the + CARDDAV:max-resource-size property value (Section 6.2.3) on the + address book collection where the resource will be stored. + +6.3.2.2. Non-Standard vCard Properties and Parameters + + vCard provides a "standard mechanism for doing non-standard things". + This extension support allows implementers to make use of non- + standard vCard properties and parameters whose names are prefixed + with the text "X-". + + Servers MUST support the use of non-standard properties and + parameters in address object resources stored via the PUT method. + + Servers may need to enforce rules for their own "private" properties + or parameters, so servers MAY reject any attempt by the client to + change those or use values for those outside of any restrictions the + server may have. A server SHOULD ensure that any "private" + + + + + +Daboo Standards Track [Page 17] + +RFC 6352 CardDAV August 2011 + + + properties or parameters it uses follow the convention of including a + vendor ID in the "X-" name, as described in Section 3.8 of [RFC2426], + e.g., "X-ABC-PRIVATE". + +6.3.2.3. Address Object Resource Entity Tag + + The DAV:getetag property MUST be defined and set to a strong entity + tag on all address object resources. + + A response to a GET request targeted at an address object resource + MUST contain an ETag response header field indicating the current + value of the strong entity tag of the address object resource. + + Servers SHOULD return a strong entity tag (ETag header) in a PUT + response when the stored address object resource is equivalent by + octet equality to the address object resource submitted in the body + of the PUT request. This allows clients to reliably use the returned + strong entity tag for data synchronization purposes. For instance, + the client can do a PROPFIND request on the stored address object + resource, have the DAV:getetag property returned, compare that value + with the strong entity tag it received on the PUT response, and know + that if they are equal, then the address object resource on the + server has not been changed. + + In the case where the data stored by a server as a result of a PUT + request is not equivalent by octet equality to the submitted address + object resource, the behavior of the ETag response header is not + specified here, with the exception that a strong entity tag MUST NOT + be returned in the response. As a result, a client may need to + retrieve the modified address object resource (and ETag) as a basis + for further changes, rather than use the address object resource it + had sent with the PUT request. + +7. Address Book Access Control + + CardDAV servers MUST support and adhere to the requirements of WebDAV + ACL [RFC3744]. WebDAV ACL provides a framework for an extensible set + of privileges that can be applied to WebDAV collections and ordinary + resources. + +7.1. Additional Principal Properties + + This section defines additional properties for WebDAV principal + resources as defined in [RFC3744]. + + + + + + + +Daboo Standards Track [Page 18] + +RFC 6352 CardDAV August 2011 + + +7.1.1. CARDDAV:addressbook-home-set Property + + Name: addressbook-home-set + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Identifies the URL of any WebDAV collections that contain + address book collections owned by the associated principal + resource. + + Protected: MAY be protected if the server has fixed locations in + which address books are created. + + COPY/MOVE behavior: This property value MUST be preserved in COPY + and MOVE operations. + + allprop behavior: SHOULD NOT be returned by a PROPFIND DAV:allprop + request. + + Description: The CARDDAV:addressbook-home-set property is meant to + allow users to easily find the address book collections owned by + the principal. Typically, users will group all the address book + collections that they own under a common collection. This + property specifies the URL of collections that are either address + book collections or ordinary collections that have child or + descendant address book collections owned by the principal. + + Definition: + + + + Example: + + + /bernard/addresses/ + + +7.1.2. CARDDAV:principal-address Property + + Name: principal-address + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Identifies the URL of an address object resource that + corresponds to the user represented by the principal. + + + + + +Daboo Standards Track [Page 19] + +RFC 6352 CardDAV August 2011 + + + Protected: MAY be protected if the server provides a fixed location + for principal addresses. + + COPY/MOVE behavior: This property value MUST be preserved in COPY + and MOVE operations. + + allprop behavior: SHOULD NOT be returned by a PROPFIND DAV:allprop + request. + + Description: The CARDDAV:principal-address property is meant to + allow users to easily find contact information for users + represented by principals on the system. This property specifies + the URL of the resource containing the corresponding contact + information. The resource could be an address object resource in + an address book collection, or it could be a resource in a + "regular" collection. + + Definition: + + + + Example: + + + /system/cyrus.vcf + + +8. Address Book Reports + + This section defines the reports that CardDAV servers MUST support on + address book collections and address object resources. + + CardDAV servers MUST advertise support for these reports on all + address book collections and address object resources with the + DAV:supported-report-set property defined in Section 3.1.5 of + [RFC3253]. CardDAV servers MAY also advertise support for these + reports on ordinary collections. + + Some of these reports allow address data (from possibly multiple + resources) to be returned. + +8.1. REPORT Method + + The REPORT method (defined in Section 3.6 of [RFC3253]) provides an + extensible mechanism for obtaining information about a resource. + Unlike the PROPFIND method, which returns the value of one or more + named properties, the REPORT method can involve more complex + + + +Daboo Standards Track [Page 20] + +RFC 6352 CardDAV August 2011 + + + processing. REPORT is valuable in cases where the server has access + to all of the information needed to perform the complex request (such + as a query), and where it would require multiple requests for the + client to retrieve the information needed to perform the same + request. + + A server that supports this specification MUST support the + DAV:expand-property report (defined in Section 3.8 of [RFC3253]). + +8.2. Ordinary Collections + + Servers MAY support the reports defined in this document on ordinary + collections (collections that are not address book collections) in + addition to address book collections or address object resources. In + computing responses to the reports on ordinary collections, servers + MUST only consider address object resources contained in address book + collections that are targeted by the REPORT based on the value of the + Depth request header. + +8.3. Searching Text: Collations + + Some of the reports defined in this section do text matches of + character strings provided by the client and compared to stored + address data. Since vCard data is by default encoded in the UTF-8 + charset and may include characters outside of the US-ASCII charset + range in some property and parameter values, there is a need to + ensure that text matching follows well-defined rules. + + To deal with this, this specification makes use of the IANA Collation + Registry defined in [RFC4790] to specify collations that may be used + to carry out the text comparison operations with a well-defined rule. + + Collations supported by the server MUST support "equality" and + "substring" match operations as per [RFC4790], Section 4.2, including + the "prefix" and "suffix" options for "substring" matching. CardDAV + uses these match options for "equals", "contains", "starts-with", and + "ends-with" match operations. + + CardDAV servers are REQUIRED to support the "i;ascii-casemap" + [RFC4790] and "i;unicode-casemap" [RFC5051] collations and MAY + support other collations. + + Servers MUST advertise the set of collations that they support via + the CARDDAV:supported-collation-set property defined on any resource + that supports reports that use collations. + + + + + + +Daboo Standards Track [Page 21] + +RFC 6352 CardDAV August 2011 + + + In the absence of a collation explicitly specified by the client, or + if the client specifies the "default" collation identifier (as + defined in [RFC4790], Section 3.1), the server MUST default to using + "i;unicode-casemap" as the collation. + + Wildcards (as defined in [RFC4790], Section 3.2) MUST NOT be used in + the collation identifier. + + If the client chooses a collation not supported by the server, the + server MUST respond with a CARDDAV:supported-collation precondition + error response. + +8.3.1. CARDDAV:supported-collation-set Property + + Name: supported-collation-set + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Identifies the set of collations supported by the server + for text matching operations. + + Protected: MUST be protected as it indicates support provided by the + server. + + COPY/MOVE behavior: This property value MUST be preserved in COPY + and MOVE operations. + + allprop behavior: SHOULD NOT be returned by a PROPFIND DAV:allprop + request. + + Description: The CARDDAV:supported-collation-set property contains + two or more CARDDAV:supported-collation elements that specify the + identifiers of the collations supported by the server. + + Definition: + + + + + + + + + + + + +Daboo Standards Track [Page 22] + +RFC 6352 CardDAV August 2011 + + + Example: + + + i;ascii-casemap + i;octet + i;unicode-casemap + + +8.4. Partial Retrieval + + Some address book reports defined in this document allow partial + retrieval of address object resources. A CardDAV client can specify + what information to return in the body of an address book REPORT + request. + + A CardDAV client can request particular WebDAV property values, all + WebDAV property values, or a list of the names of the resource's + WebDAV properties. A CardDAV client can also request address data to + be returned and whether all vCard properties should be returned or + only particular ones. See CARDDAV:address-data in Section 10.4. + +8.5. Non-Standard Properties and Parameters + + Servers MUST support the use of non-standard vCard property or + parameter names in the CARDDAV:address-data XML element in address + book REPORT requests to allow clients to request that non-standard + properties and parameters be returned in the address data provided in + the response. + + Servers MAY support the use of non-standard vCard property or + parameter names in the CARDDAV:prop-filter and CARDDAV:param-filter + XML elements specified in the CARDDAV:filter XML element of address + book REPORT requests. + + Servers MUST fail with the CARDDAV:supported-filter precondition if + an address book REPORT request uses a CARDDAV:prop-filter or + CARDDAV:param-filter XML element that makes reference to a non- + standard vCard property or parameter name on which the server does + not support queries. + +8.6. CARDDAV:addressbook-query Report + + The CARDDAV:addressbook-query REPORT performs a search for all + address object resources that match a specified filter. The response + of this report will contain all the WebDAV properties and address + object resource data specified in the request. In the case of the + + + + +Daboo Standards Track [Page 23] + +RFC 6352 CardDAV August 2011 + + + CARDDAV:address-data XML element, one can explicitly specify the + vCard properties that should be returned in the address object + resource data that matches the filter. + + The format of this report is modeled on the PROPFIND method. The + request and response bodies of the CARDDAV:addressbook-query report + use XML elements that are also used by PROPFIND. In particular, the + request can include XML elements to request WebDAV properties to be + returned. When that occurs, the response should follow the same + behavior as PROPFIND with respect to the DAV:multistatus response + elements used to return specific WebDAV property results. For + instance, a request to retrieve the value of a WebDAV property that + does not exist is an error and MUST be noted with a response XML + element that contains a 404 (Not Found) status value. + + Support for the CARDDAV:addressbook-query REPORT is REQUIRED. + + Marshalling: + + The request body MUST be a CARDDAV:addressbook-query XML element + as defined in Section 10.3. + + The request MUST include a Depth header. The scope of the query + is determined by the value of the Depth header. For example, to + query all address object resources in an address book collection, + the REPORT would use the address book collection as the Request- + URI and specify a Depth of 1 or infinity. + + The response body for a successful request MUST be a + DAV:multistatus XML element (i.e., the response uses the same + format as the response for PROPFIND). In the case where there are + no response elements, the returned DAV:multistatus XML element is + empty. + + The response body for a successful CARDDAV:addressbook-query + REPORT request MUST contain a DAV:response element for each + address object that matched the search filter. Address data is + returned in the CARDDAV:address-data XML element inside the + DAV:propstat XML element. + + Preconditions: + + (CARDDAV:supported-address-data): The attributes "content-type" + and "version" of the CARDDAV:address-data XML element (see + Section 10.4) specify a media type supported by the server for + address object resources. + + + + + +Daboo Standards Track [Page 24] + +RFC 6352 CardDAV August 2011 + + + (CARDDAV:supported-filter): The CARDDAV:prop-filter (see + Section 10.5.1) and CARDDAV:param-filter (see Section 10.5.2) XML + elements used in the CARDDAV:filter XML element (see Section 10.5) + in the REPORT request only make reference to vCard properties and + parameters for which queries are supported by the server. That + is, if the CARDDAV:filter element attempts to reference an + unsupported vCard property or parameter, this precondition is + violated. A server SHOULD report the CARDDAV:prop-filter or + CARDDAV:param-filter for which it does not provide support. + + + + (CARDDAV:supported-collation): Any XML attribute specifying a + collation MUST specify a collation supported by the server as + described in Section 8.3. + + Postconditions: + + (DAV:number-of-matches-within-limits): The number of matching + address object resources must fall within server-specific, + predefined limits. For example, this condition might be triggered + if a search specification would cause the return of an extremely + large number of responses. + +8.6.1. Limiting Results + + A client can limit the number of results returned by the server + through use of the CARDDAV:limit element in the request body. This + is useful when clients are only interested in a few matches or only + have limited space to display results to users and thus don't need + the overhead of receiving more than that. When the results are + truncated by the server, the server MUST follow the rules below for + indicating a result set truncation to the client. + +8.6.2. Truncation of Results + + A server MAY limit the number of resources in a response, for + example, to limit the amount of work expended in processing a query, + or as the result of an explicit limit set by the client. If the + result set is truncated because of such a limit, the response MUST + use status code 207 (Multi-Status), return a DAV:multistatus response + body, and indicate a status of 507 (Insufficient Storage) for the + Request-URI. That DAV:response element SHOULD include a DAV:error + element with the DAV:number-of-matches-within-limits precondition, as + defined in [RFC3744], Section 9.2. + + + + + +Daboo Standards Track [Page 25] + +RFC 6352 CardDAV August 2011 + + + The server SHOULD also include the partial results in additional + DAV:response elements. If a client-requested limit is being applied, + the 507 response for the Request-URI MUST NOT be included in + calculating the limit (e.g., if the client requests that only a + single result be returned, and multiple matches are present, then the + DAV:multistatus response will include one DAV:response for the + matching resource and one DAV:response for the 507 status on the + Request-URI). + +8.6.3. Example: Partial Retrieval of vCards Matching NICKNAME + + In this example, the client requests that the server search for + address object resources that contain a NICKNAME property whose value + equals some specific text and return specific vCard properties for + those vCards found. In addition, the DAV:getetag property is also + requested and returned as part of the response. + + >> Request << + + REPORT /home/bernard/addressbook/ HTTP/1.1 + Host: addressbook.example.com + Depth: 1 + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + + + me + + + + + + + + +Daboo Standards Track [Page 26] + +RFC 6352 CardDAV August 2011 + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + /home/bernard/addressbook/v102.vcf + + + "23ba4d-ff11fb" + BEGIN:VCARD + VERSION:3.0 + NICKNAME:me + UID:34222-232@example.com + FN:Cyrus Daboo + EMAIL:daboo@example.com + END:VCARD + + + HTTP/1.1 200 OK + + + + +8.6.4. Example: Partial Retrieval of vCards Matching a Full Name or + Email Address + + In this example, the client requests that the server search for + address object resources that contain a FN property whose value + contains some specific text or that contain an EMAIL property whose + value contains other text and return specific vCard properties for + those vCards found. In addition, the DAV:getetag property is also + requested and returned as part of the response. + + >> Request << + + REPORT /home/bernard/addressbook/ HTTP/1.1 + Host: addressbook.example.com + Depth: 1 + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + +Daboo Standards Track [Page 27] + +RFC 6352 CardDAV August 2011 + + + + + + + + + + + + + + + + + daboo + + + daboo + + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + /home/bernard/addressbook/v102.vcf + + + "23ba4d-ff11fb" + BEGIN:VCARD + VERSION:3.0 + NICKNAME:me + UID:34222-232@example.com + FN:David Boo + EMAIL:daboo@example.com + + + +Daboo Standards Track [Page 28] + +RFC 6352 CardDAV August 2011 + + + END:VCARD + + + HTTP/1.1 200 OK + + + + /home/bernard/addressbook/v104.vcf + + + "23ba4d-ff11fc" + BEGIN:VCARD + VERSION:3.0 + NICKNAME:oliver + UID:34222-23222@example.com + FN:Oliver Daboo + EMAIL:oliver@example.com + END:VCARD + + + HTTP/1.1 200 OK + + + + +8.6.5. Example: Truncated Results + + In this example, the client requests that the server search for + address object resources that contain a FN property whose value + contains some specific text and return the DAV:getetag property for + two results only. The server response includes a 507 status for the + Request-URI indicating that there were more than two resources that + matched the query, but that the server truncated the result set as + requested by the client. + + >> Request << + + REPORT /home/bernard/addressbook/ HTTP/1.1 + Host: addressbook.example.com + Depth: 1 + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + +Daboo Standards Track [Page 29] + +RFC 6352 CardDAV August 2011 + + + + + + + + daboo + + + + 2 + + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + /home/bernard/addressbook/ + HTTP/1.1 507 Insufficient Storage + + + Only two matching records were returned + + + + /home/bernard/addressbook/v102.vcf + + + "23ba4d-ff11fb" + + HTTP/1.1 200 OK + + + + /home/bernard/addressbook/v104.vcf + + + "23ba4d-ff11fc" + + + + +Daboo Standards Track [Page 30] + +RFC 6352 CardDAV August 2011 + + + HTTP/1.1 200 OK + + + + +8.7. CARDDAV:addressbook-multiget Report + + The CARDDAV:addressbook-multiget REPORT is used to retrieve specific + address object resources from within a collection, if the Request-URI + is a collection, or to retrieve a specific address object resource, + if the Request-URI is an address object resource. This report is + similar to the CARDDAV:addressbook-query REPORT (see Section 8.6), + except that it takes a list of DAV:href elements instead of a + CARDDAV:filter element to determine which address object resources to + return. + + Support for the addressbook-multiget REPORT is REQUIRED. + + Marshalling: + + The request body MUST be a CARDDAV:addressbook-multiget XML + element (see Section 10.7), which MUST contain at least one + DAV:href XML element and one optional CARDDAV:address-data element + as defined in Section 10.4. If DAV:href elements are present, the + scope of the request is the set of resources identified by these + elements, which all need to be members (not necessarily internal + members) of the resource identified by the Request-URI. + Otherwise, the scope is the resource identified by the Request-URI + itself. + + The request MUST include a Depth: 0 header; however, the actual + scope of the REPORT is determined as described above. + + The response body for a successful request MUST be a + DAV:multistatus XML element. + + The response body for a successful CARDDAV:addressbook-multiget + REPORT request MUST contain a DAV:response element for each + address object resource referenced by the provided set of DAV:href + elements. Address data is returned in the CARDDAV:address-data + element inside the DAV:prop element. + + In the case of an error accessing any of the provided DAV:href + resources, the server MUST return the appropriate error status + code in the DAV:status element of the corresponding DAV:response + element. + + + + + +Daboo Standards Track [Page 31] + +RFC 6352 CardDAV August 2011 + + + Preconditions: + + (CARDDAV:supported-address-data): The attributes "content-type" + and "version" of the CARDDAV:address-data XML elements (see + Section 10.4) specify a media type supported by the server for + address object resources. + + Postconditions: + + None. + +8.7.1. Example: CARDDAV:addressbook-multiget Report + + In this example, the client requests the server to return specific + vCard properties of the address components referenced by specific + URIs. In addition, the DAV:getetag property is also requested and + returned as part of the response. Note that, in this example, the + resource at + http://addressbook.example.com/home/bernard/addressbook/vcf1.vcf does + not exist, resulting in an error status response. + + >> Request << + + REPORT /home/bernard/addressbook/ HTTP/1.1 + Host: addressbook.example.com + Depth: 1 + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + + + + + + + /home/bernard/addressbook/vcf102.vcf + /home/bernard/addressbook/vcf1.vcf + + + + + + + +Daboo Standards Track [Page 32] + +RFC 6352 CardDAV August 2011 + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + /home/bernard/addressbook/vcf102.vcf + + + "23ba4d-ff11fb" + BEGIN:VCARD + VERSION:3.0 + NICKNAME:me + UID:34222-232@example.com + FN:Cyrus Daboo + EMAIL:daboo@example.com + END:VCARD + + + HTTP/1.1 200 OK + + + + /home/bernard/addressbook/vcf1.vcf + HTTP/1.1 404 Resource not found + + + +8.7.2. Example: CARDDAV:addressbook-multiget Report + + In this example, the client requests the server to return vCard v4.0 + data of the address components referenced by specific URIs. In + addition, the DAV:getetag property is also requested and returned as + part of the response. Note that, in this example, the resource at + http://addressbook.example.com/home/bernard/addressbook/vcf3.vcf + exists but in a media type format that the server is unable to + convert, resulting in an error status response. + + + + + + + + + +Daboo Standards Track [Page 33] + +RFC 6352 CardDAV August 2011 + + + >> Request << + + REPORT /home/bernard/addressbook/ HTTP/1.1 + Host: addressbook.example.com + Depth: 1 + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + + + + /home/bernard/addressbook/vcf3.vcf + + + >> Response << + + HTTP/1.1 207 Multi-Status + Date: Sat, 11 Nov 2006 09:32:12 GMT + Content-Type: text/xml; charset="utf-8" + Content-Length: xxxx + + + + + /home/bernard/addressbook/vcf3.vcf + HTTP/1.1 415 Unsupported Media Type + + Unable to convert from vCard v3.0 + to vCard v4.0 + + + +9. Client Guidelines + +9.1. Restrict the Properties Returned + + Clients may not need all the properties in a vCard object when + presenting information to the user, or looking up specific items for + their email address, for example. Since some property data can be + large (e.g., PHOTO or SOUND with in-line content) clients can choose + to ignore those by only requesting the specific items it knows it + will use, through use of the CARDDAV:address-data XML element in the + relevant reports. + + + +Daboo Standards Track [Page 34] + +RFC 6352 CardDAV August 2011 + + + However, if a client needs to make a change to a vCard, it can only + change the entire vCard data via a PUT request. There is no way to + incrementally make a change to a set of properties within a vCard + object resource. As a result, the client will have to cache the + entire set of properties on a resource that is being changed. + +9.2. Avoiding Lost Updates + + When resources are accessed by multiple clients, the possibility of + clients overwriting each other's changes exists. To alleviate this, + clients SHOULD use the If-Match request header on PUT requests with + the ETag of the previously retrieved resource data to check whether + the resource was modified since it was previously retrieved. If a + precondition failure occurs, clients need to reload the resource and + go through their own merge or conflict resolution process before + writing back the data (again using the If-Match check). + +9.3. Client Configuration + + When CardDAV clients need to be configured, the key piece of + information that they require is the principal-URL of the user whose + address book information is desired. Servers SHOULD support the + DAV:current-user-principal-URL property as defined in [RFC5397] to + give clients a fast way to locate user principals. + + Given support for SRV records (Section 11) and DAV:current-user- + principal-URL [RFC5397], users only need enter a user identifier, + host name, and password to configure their client. The client would + take the host name and do an SRV lookup to locate the CardDAV server, + then execute an authenticated PROPFIND on the root/resource looking + for the DAV:current-user-principal-URL property. The value returned + gives the client direct access to the user's principal-URL and from + there all the related CardDAV properties needed to locate address + books. + +9.4. Finding Other Users' Address Books + + For use cases of address book sharing, one might wish to find the + address book belonging to another user. To find other users' address + books on the same server, the DAV:principal-property-search REPORT + [RFC3744] can be used to search principals for matching properties + and return specified properties for the matching principal resources. + To search for an address book owned by a user named "Laurie", the + REPORT request body would look like this: + + + + + + + +Daboo Standards Track [Page 35] + +RFC 6352 CardDAV August 2011 + + + + + + + + + Laurie + + + + + + + + The server performs a case-sensitive or caseless search for a + matching string subset of "Laurie" within the DAV:displayname + property. Thus, the server might return "Laurie Dusseault", "Laurier + Desruisseaux", or "Wilfrid Laurier" all as matching DAV:displayname + values, and the address books for each of these. + +10. XML Element Definitions + +10.1. CARDDAV:addressbook XML Element + + Name: addressbook + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies the resource type of an address book collection. + + Description: See Section 5.2. + + Definition: + + + +10.2. CARDDAV:supported-collation XML Element + + Name: supported-collation + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Identifies a single collation via its collation identifier + as defined by [RFC4790]. + + Description: The CARDDAV:supported-collation contains the text of a + collation identifier as described in Section 8.3.1. + + + +Daboo Standards Track [Page 36] + +RFC 6352 CardDAV August 2011 + + + Definition: + + + + +10.3. CARDDAV:addressbook-query XML Element + + Name: addressbook-query + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Defines a report for querying address book data + + Description: See Section 8.6. + + Definition: + + + +10.4. CARDDAV:address-data XML Element + + Name: address-data + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies one of the following: + + 1. The parts of an address object resource that should be + returned by a given address book REPORT request, and the media + type and version for the returned data; or + + 2. The content of an address object resource in a response to an + address book REPORT request. + + Description: When used in an address book REPORT request, the + CARDDAV:address-data XML element specifies which parts of address + object resources need to be returned in the response. If the + CARDDAV:address-data XML element doesn't contain any CARDDAV:prop + elements, address object resources will be returned in their + entirety. Additionally, a media type and version can be specified + to request that the server return the data in that format if + possible. + + Finally, when used in an address book REPORT response, the + CARDDAV:address-data XML element specifies the content of an + address object resource. Given that XML parsers normalize the + + + +Daboo Standards Track [Page 37] + +RFC 6352 CardDAV August 2011 + + + two-character sequence CRLF (US-ASCII decimal 13 and US-ASCII + decimal 10) to a single LF character (US-ASCII decimal 10), the CR + character (US-ASCII decimal 13) MAY be omitted in address object + resources specified in the CARDDAV:address-data XML element. + Furthermore, address object resources specified in the + CARDDAV:address-data XML element MAY be invalid per their media + type specification if the CARDDAV:address-data XML element part of + the address book REPORT request did not specify required vCard + properties (e.g., UID, etc.) or specified a CARDDAV:prop XML + element with the "novalue" attribute set to "yes". + + Note: The CARDDAV:address-data XML element is specified in requests + and responses inside the DAV:prop XML element as if it were a + WebDAV property. However, the CARDDAV:address-data XML element is + not a WebDAV property and as such it is not returned in PROPFIND + responses nor used in PROPPATCH requests. + + Note: The address data embedded within the CARDDAV:address-data XML + element MUST follow the standard XML character data encoding + rules, including use of <, >, & etc., entity encoding or + the use of a construct. In the latter case, the + vCard data cannot contain the character sequence "]]>", which is + the end delimiter for the CDATA section. + + Definition: + + + + when nested in the DAV:prop XML element in an address book + REPORT request to specify which parts of address object + resources should be returned in the response; + + + + + when nested in the DAV:prop XML element in an address book + REPORT response to specify the content of a returned + address object resource. + + + + + + attributes can be used on each variant of the + CALDAV:address-data XML element. + + + + + +Daboo Standards Track [Page 38] + +RFC 6352 CardDAV August 2011 + + +10.4.1. CARDDAV:allprop XML Element + + Name: allprop + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies that all vCard properties shall be returned. + + Description: This element can be used when the client wants all + vCard properties of components returned by a report. + + Definition: + + + + Note: The CARDDAV:allprop element defined here has the same name as + the DAV:allprop element defined in WebDAV. However, the + CARDDAV:allprop element defined here uses the + "urn:ietf:params:xml:ns:carddav" namespace, as opposed to the "DAV:" + namespace used for the DAV:allprop element defined in WebDAV. + +10.4.2. CARDDAV:prop XML Element + + Name: prop + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Defines which vCard properties to return in the response. + + Description: The "name" attribute specifies the name of the vCard + property to return (e.g., "NICKNAME"). The "novalue" attribute + can be used by clients to request that the actual value of the + property not be returned (if the "novalue" attribute is set to + "yes"). In that case, the server will return just the vCard + property name and any vCard parameters and a trailing ":" without + the subsequent value data. + + vCard allows a "group" prefix to appear before a property name in + the vCard data. When the "name" attribute does not specify a + group prefix, it MUST match properties in the vCard data without a + group prefix or with any group prefix. When the "name" attribute + includes a group prefix, it MUST match properties that have + exactly the same group prefix and name. For example, a "name" set + to "TEL" will match "TEL", "X-ABC.TEL", and "X-ABC-1.TEL" vCard + properties. A "name" set to "X-ABC.TEL" will match an "X-ABC.TEL" + vCard property only; it will not match "TEL" or "X-ABC-1.TEL". + + + + + +Daboo Standards Track [Page 39] + +RFC 6352 CardDAV August 2011 + + + Definition: + + + + + + + + Note: The CARDDAV:prop element defined here has the same name as the + DAV:prop element defined in WebDAV. However, the CARDDAV:prop + element defined here uses the "urn:ietf:params:xml:ns:carddav" + namespace, as opposed to the "DAV:" namespace used for the DAV:prop + element defined in WebDAV. + +10.5. CARDDAV:filter XML Element + + Name: filter + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Determines which matching objects are returned. + + Description: The "filter" element specifies the search filter used + to match address objects that should be returned by a report. The + "test" attribute specifies whether any (logical OR) or all + (logical AND) of the prop-filter tests need to match in order for + the overall filter to match. + + Definition: + + + + + + +10.5.1. CARDDAV:prop-filter XML Element + + Name: prop-filter + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Limits the search to specific vCard properties. + + + + + + +Daboo Standards Track [Page 40] + +RFC 6352 CardDAV August 2011 + + + Description: The CARDDAV:prop-filter XML element specifies search + criteria on a specific vCard property (e.g., "NICKNAME"). An + address object is said to match a CARDDAV:prop-filter if: + + * A vCard property of the type specified by the "name" attribute + exists, and the CARDDAV:prop-filter is empty, or it matches any + specified CARDDAV:text-match or CARDDAV:param-filter + conditions. The "test" attribute specifies whether any + (logical OR) or all (logical AND) of the text-filter and param- + filter tests need to match in order for the overall filter to + match. + + or: + + * A vCard property of the type specified by the "name" attribute + does not exist, and the CARDDAV:is-not-defined element is + specified. + + vCard allows a "group" prefix to appear before a property name in + the vCard data. When the "name" attribute does not specify a + group prefix, it MUST match properties in the vCard data without a + group prefix or with any group prefix. When the "name" attribute + includes a group prefix, it MUST match properties that have + exactly the same group prefix and name. For example, a "name" set + to "TEL" will match "TEL", "X-ABC.TEL", "X-ABC-1.TEL" vCard + properties. A "name" set to "X-ABC.TEL" will match an "X-ABC.TEL" + vCard property only, it will not match "TEL" or "X-ABC-1.TEL". + + Definition: + + + + + + +10.5.2. CARDDAV:param-filter XML Element + + Name: param-filter + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Limits the search to specific parameter values. + + + + +Daboo Standards Track [Page 41] + +RFC 6352 CardDAV August 2011 + + + Description: The CARDDAV:param-filter XML element specifies search + criteria on a specific vCard property parameter (e.g., TYPE) in + the scope of a given CARDDAV:prop-filter. A vCard property is + said to match a CARDDAV:param-filter if: + + * A parameter of the type specified by the "name" attribute + exists, and the CARDDAV:param-filter is empty, or it matches + the CARDDAV:text-match conditions if specified. + + or: + + * A parameter of the type specified by the "name" attribute does + not exist, and the CARDDAV:is-not-defined element is specified. + + Definition: + + + + + + +10.5.3. CARDDAV:is-not-defined XML Element + + Name: is-not-defined + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies that a match should occur if the enclosing vCard + property or parameter does not exist. + + Description: The CARDDAV:is-not-defined XML element specifies that a + match occurs if the enclosing vCard property or parameter value + specified in an address book REPORT request does not exist in the + address data being tested. + + Definition: + + + +10.5.4. CARDDAV:text-match XML Element + + Name: text-match + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies a substring match on a vCard property or + parameter value. + + + + +Daboo Standards Track [Page 42] + +RFC 6352 CardDAV August 2011 + + + Description: The CARDDAV:text-match XML element specifies text used + for a substring match against the vCard property or parameter + value specified in an address book REPORT request. + + The "collation" attribute is used to select the collation that the + server MUST use for character string matching. In the absence of + this attribute, the server MUST use the "i;unicode-casemap" + collation. + + The "negate-condition" attribute is used to indicate that this + test returns a match if the text matches, when the attribute value + is set to "no", or return a match if the text does not match, if + the attribute value is set to "yes". For example, this can be + used to match components with a CATEGORIES property not set to + PERSON. + + The "match-type" attribute is used to indicate the type of match + operation to use. Possible choices are: + + "equals" - an exact match to the target string + + "contains" - a substring match, matching anywhere within the + target string + + "starts-with" - a substring match, matching only at the start + of the target string + + "ends-with" - a substring match, matching only at the end of + the target string + + Definition: + + + + + + +10.6. CARDDAV:limit XML Element + + Name: limit + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies different types of limits that can be applied to + the results returned by the server. + + + +Daboo Standards Track [Page 43] + +RFC 6352 CardDAV August 2011 + + + Description: The CARDDAV:limit XML element can be used to specify + different types of limits that the client can request the server + to apply to the results returned by the server. Currently, only + the CARDDAV:nresults limit can be used; other types of limit could + be defined in the future. + + Definition: + + + +10.6.1. CARDDAV:nresults XML Element + + Name: nresults + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: Specifies a limit on the number of results returned by the + server. + + Description: The CARDDAV:nresults XML element contains a requested + maximum number of DAV:response elements to be returned in the + response body of a query. The server MAY disregard this limit. + The value of this element is an unsigned integer. + + Definition: + + + + +10.7. CARDDAV:addressbook-multiget XML Element + + Name: addressbook-multiget + + Namespace: urn:ietf:params:xml:ns:carddav + + Purpose: CardDAV report used to retrieve specific address objects + via their URIs. + + Description: See Section 8.7. + + Definition: + + + + + + + +Daboo Standards Track [Page 44] + +RFC 6352 CardDAV August 2011 + + +11. Service Discovery via SRV Records + + [RFC2782] defines a DNS-based service discovery protocol that has + been widely adopted as a means of locating particular services within + a local area network and beyond, using SRV RRs. + + This specification adds two service types for use with SRV records: + + carddav: Identifies a CardDAV server that uses HTTP without TLS + [RFC2818]. + + carddavs: Identifies a CardDAV server that uses HTTP with TLS + [RFC2818]. + + Example: non-TLS service record + + _carddav._tcp SRV 0 1 80 addressbook.example.com. + + Example: TLS service + + _carddavs._tcp SRV 0 1 443 addressbook.example.com. + +12. Internationalization Considerations + + CardDAV allows internationalized strings to be stored and retrieved + for the description of address book collections (see Section 6.2.1). + + The CARDDAV:addressbook-query REPORT (Section 8.6) includes a text + searching option controlled by the CARDDAV:text-match element and + details of character handling are covered in the description of that + element (see Section 10.5.4). + +13. Security Considerations + + HTTP protocol transactions are sent in the clear over the network + unless protection from snooping is negotiated. This can be + accomplished by use of TLS as defined in [RFC2818]. In particular, + if HTTP Basic authentication [RFC2617] is available, the server MUST + allow TLS to be used at the same time, and it SHOULD prevent use of + Basic authentication when TLS is not in use. Clients SHOULD use TLS + whenever possible. + + With the ACL extension [RFC3744] present, WebDAV allows control over + who can access (read or write) any resource on the WebDAV server. In + addition, WebDAV ACL provides for an "inheritance" mechanism, whereby + resources may inherit access privileges from other resources. Often, + the "other" resource is a parent collection of the resource itself. + Servers are able to support address books that are "private" + + + +Daboo Standards Track [Page 45] + +RFC 6352 CardDAV August 2011 + + + (accessible only to the "owner"), "shared" (accessible to the owner + and other specified authenticated users), and "public" (accessible to + any authenticated or unauthenticated users). When provisioning + address books of a particular type, servers MUST ensure that the + correct privileges are applied on creation. In particular, private + and shared address books MUST NOT be accessible by unauthenticated + users (to prevent data from being automatically searched or indexed + by web "crawlers"). + + Clients SHOULD warn users in an appropriate fashion when they copy or + move address data from a private address book to a shared address + book or public address book. Clients SHOULD provide a clear + indication as to which address books are private, shared, or public. + Clients SHOULD provide an appropriate warning when changing access + privileges for a private or shared address book with data so as to + allow unauthenticated users access. + + This specification currently relies on standard HTTP authentication + mechanisms for identifying users. These comprise Basic and Digest + authentication [RFC2617] as well as TLS [RFC2818] using client-side + certificates. + +14. IANA Consideration + + This document uses a URN to describe a new XML namespace conforming + to the registry mechanism described in [RFC3688]. + +14.1. Namespace Registration + + Registration request for the carddav namespace: + + URI: urn:ietf:params:xml:ns:carddav + + Registrant Contact: The IESG + + XML: None - not applicable for namespace registrations. + +15. Acknowledgments + + Thanks go to Lisa Dusseault and Bernard Desruisseaux for their work + on CalDAV, on which CardDAV is heavily based. The following + individuals contributed their ideas and support for writing this + specification: Mike Douglass, Stefan Eissing, Helge Hess, Arnaud + Quillaud, Julian Reschke, Elias Sinderson, Greg Stein, Wilfredo + Sanchez, and Simon Vaillancourt. + + + + + + +Daboo Standards Track [Page 46] + +RFC 6352 CardDAV August 2011 + + +16. References + +16.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2426] Dawson, F. and T. Howes, "vCard MIME Directory Profile", + RFC 2426, September 1998. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + + [RFC2617] Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., + Leach, P., Luotonen, A., and L. Stewart, "HTTP + Authentication: Basic and Digest Access Authentication", + RFC 2617, June 1999. + + [RFC2782] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR for + specifying the location of services (DNS SRV)", RFC 2782, + February 2000. + + [RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, May 2000. + + [RFC3253] Clemm, G., Amsden, J., Ellison, T., Kaler, C., and J. + Whitehead, "Versioning Extensions to WebDAV + (Web Distributed Authoring and Versioning)", RFC 3253, + March 2002. + + [RFC3688] Mealling, M., "The IETF XML Registry", BCP 81, RFC 3688, + January 2004. + + [RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. Whitehead, "Web + Distributed Authoring and Versioning (WebDAV) + Access Control Protocol", RFC 3744, May 2004. + + [RFC4790] Newman, C., Duerst, M., and A. Gulbrandsen, "Internet + Application Protocol Collation Registry", RFC 4790, + March 2007. + + [RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed + Authoring and Versioning (WebDAV)", RFC 4918, June 2007. + + [RFC5051] Crispin, M., "i;unicode-casemap - Simple Unicode Collation + Algorithm", RFC 5051, October 2007. + + + + + +Daboo Standards Track [Page 47] + +RFC 6352 CardDAV August 2011 + + + [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security + (TLS) Protocol Version 1.2", RFC 5246, August 2008. + + [RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S., + Housley, R., and W. Polk, "Internet X.509 Public Key + Infrastructure Certificate and Certificate Revocation List + (CRL) Profile", RFC 5280, May 2008. + + [RFC5397] Sanchez, W. and C. Daboo, "WebDAV Current Principal + Extension", RFC 5397, December 2008. + + [RFC5689] Daboo, C., "Extended MKCOL for Web Distributed Authoring + and Versioning (WebDAV)", RFC 5689, September 2009. + + [RFC6350] Perreault, S., "vCard Format Specification", RFC 6350, + August 2011. + + [W3C.REC-xml-20081126] + Bray, T., Paoli, J., Sperberg-McQueen, C., Maler, E., and + F. Yergeau, "Extensible Markup Language (XML) 1.0 (Fifth + Edition)", World Wide Web Consortium Recommendation REC- + xml-20081126, November 2008, + . + +16.2. Informative References + + [IMSP] Myers, J., "IMSP - Internet Message Support Protocol", + Work in Progress, June 1995. + + [RFC2244] Newman, C. and J. Myers, "ACAP -- Application + Configuration Access Protocol", RFC 2244, November 1997. + + [RFC4510] Zeilenga, K., "Lightweight Directory Access Protocol + (LDAP): Technical Specification Road Map", RFC 4510, + June 2006. + +Author's Address + + Cyrus Daboo + Apple, Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + EMail: cyrus@daboo.name + URI: http://www.apple.com/ + + + + + +Daboo Standards Track [Page 48] + diff --git a/doc/rfc6764-caldav-carddav-service-discovery.txt b/doc/rfc6764-caldav-carddav-service-discovery.txt new file mode 100644 index 00000000..aed17a83 --- /dev/null +++ b/doc/rfc6764-caldav-carddav-service-discovery.txt @@ -0,0 +1,787 @@ + + + + + + +Internet Engineering Task Force (IETF) C. Daboo +Request for Comments: 6764 Apple Inc. +Updates: 4791, 6352 February 2013 +Category: Standards Track +ISSN: 2070-1721 + + + Locating Services for Calendaring Extensions to + WebDAV (CalDAV) and vCard Extensions to WebDAV (CardDAV) + +Abstract + + This specification describes how DNS SRV records, DNS TXT records, + and well-known URIs can be used together or separately to locate + CalDAV (Calendaring Extensions to Web Distributed Authoring and + Versioning (WebDAV)) or CardDAV (vCard Extensions to WebDAV) + services. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc6764. + +Copyright Notice + + Copyright (c) 2013 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + + + + +Daboo Standards Track [Page 1] + +RFC 6764 SRV for CalDAV & CardDAV February 2013 + + +Table of Contents + + 1. Introduction ....................................................2 + 2. Conventions Used in This Document ...............................3 + 3. CalDAV SRV Service Labels .......................................3 + 4. CalDAV and CardDAV Service TXT Records ..........................4 + 5. CalDAV and CardDAV Service Well-Known URI .......................4 + 5.1. Example: Well-Known URI Redirects to Actual + "Context Path" .............................................5 + 6. Client "Bootstrapping" Procedures ...............................5 + 7. Guidance for Service Providers ..................................8 + 8. Security Considerations .........................................9 + 9. IANA Considerations .............................................9 + 9.1. Well-Known URI Registrations ...............................9 + 9.1.1. caldav Well-Known URI Registration .................10 + 9.1.2. carddav Well-Known URI Registration ................10 + 9.2. Service Name Registrations ................................10 + 9.2.1. caldav Service Name Registration ...................10 + 9.2.2. caldavs Service Name Registration ..................11 + 9.2.3. carddav Service Name Registration ..................11 + 9.2.4. carddavs Service Name Registration .................12 + 10. Acknowledgments ...............................................12 + 11. References ....................................................12 + 11.1. Normative References .....................................12 + 11.2. Informative References ...................................14 + +1. Introduction + + [RFC4791] defines the CalDAV calendar access protocol, based on HTTP + [RFC2616], for accessing calendar data stored on a server. CalDAV + clients need to be able to discover appropriate CalDAV servers within + their local area network and at other domains, e.g., to minimize the + need for end users to know specific details such as the fully + qualified domain name (FQDN) and port number for their servers. + + [RFC6352] defines the CardDAV address book access protocol based on + HTTP [RFC2616], for accessing contact data stored on a server. As + with CalDAV, clients also need to be able to discover CardDAV + servers. + + [RFC2782] defines a DNS-based service discovery protocol that has + been widely adopted as a means of locating particular services within + a local area network and beyond, using DNS SRV Resource Records + (RRs). This has been enhanced to provide additional service meta- + data by use of DNS TXT RRs as per [RFC6763]. + + + + + + +Daboo Standards Track [Page 2] + +RFC 6764 SRV for CalDAV & CardDAV February 2013 + + + This specification defines new SRV service types for the CalDAV + protocol and gives an example of how clients can use this together + with other protocol features to enable simple client configuration. + SRV service types for CardDAV are already defined in Section 11 of + [RFC6352]. + + Another issue with CalDAV or CardDAV service discovery is that the + service might not be located at the "root" URI of the HTTP server + hosting it. Thus, a client needs to be able to determine the + complete path component of the Request-URI to use in HTTP requests: + the "context path". For example, if CalDAV is implemented as a + "servlet" in a web server "container", the servlet "context path" + might be "/caldav/". So the URI for the CalDAV service would be, + e.g., "http://caldav.example.com/caldav/" rather than + "http://caldav.example.com/". SRV RRs by themselves only provide an + FQDN and port number for the service, not a path. Since the client + "bootstrapping" process requires initial access to the "context path" + of the service, there needs to be a simple way for clients to also + discover what that path is. + + This specification makes use of the "well-known URI" feature + [RFC5785] of HTTP servers to provide a well-known URI for CalDAV or + CardDAV services that clients can use. The well-known URI will point + to a resource on the server that is simply a "stub" resource that + provides a redirect to the actual "context path" resource + representing the service endpoint. + +2. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + +3. CalDAV SRV Service Labels + + This specification adds two SRV service labels for use with CalDAV: + + _caldav: Identifies a CalDAV server that uses HTTP without + Transport Layer Security (TLS) [RFC2818]. + + _caldavs: Identifies a CalDAV server that uses HTTP with TLS + [RFC2818]. + + + + + + + + + +Daboo Standards Track [Page 3] + +RFC 6764 SRV for CalDAV & CardDAV February 2013 + + + Clients MUST honor Priority and Weight values in the SRV RRs, as + described by [RFC2782]. + + Example: service record for server without TLS + + _caldav._tcp SRV 0 1 80 calendar.example.com. + + Example: service record for server with TLS + + _caldavs._tcp SRV 0 1 443 calendar.example.com. + +4. CalDAV and CardDAV Service TXT Records + + When SRV RRs are used to advertise CalDAV and CardDAV services, it is + also convenient to be able to specify a "context path" in the DNS to + be retrieved at the same time. To enable that, this specification + uses a TXT RR that follows the syntax defined in Section 6 of + [RFC6763] and defines a "path" key for use in that record. The value + of the key MUST be the actual "context path" to the corresponding + service on the server. + + A site might provide TXT records in addition to SRV records for each + service. When present, clients MUST use the "path" value as the + "context path" for the service in HTTP requests. When not present, + clients use the ".well-known" URI approach described next. + + Example: text record for service with TLS + + _caldavs._tcp TXT path=/caldav + +5. CalDAV and CardDAV Service Well-Known URI + + Two ".well-known" URIs are registered by this specification for + CalDAV and CardDAV services, "caldav" and "carddav" respectively (see + Section 9). These URIs point to a resource that the client can use + as the initial "context path" for the service they are trying to + connect to. The server MUST redirect HTTP requests for that resource + to the actual "context path" using one of the available mechanisms + provided by HTTP (e.g., using a 301, 303, or 307 response). Clients + MUST handle HTTP redirects on the ".well-known" URI. Servers MUST + NOT locate the actual CalDAV or CardDAV service endpoint at the + ".well-known" URI as per Section 1.1 of [RFC5785]. + + Servers SHOULD set an appropriate Cache-Control header value (as per + Section 14.9 of [RFC2616]) in the redirect response to ensure caching + occurs or does not occur as needed or as required by the type of + response generated. For example, if it is anticipated that the + + + + +Daboo Standards Track [Page 4] + +RFC 6764 SRV for CalDAV & CardDAV February 2013 + + + location of the redirect might change over time, then a "no-cache" + value would be used. + + To facilitate "context paths" that might differ from user to user, + the server MAY require authentication when a client tries to access + the ".well-known" URI (i.e., the server would return a 401 status + response to the unauthenticated request from the client, then return + the redirect response only after a successful authentication by the + client). + +5.1. Example: Well-Known URI Redirects to Actual "Context Path" + + A CalDAV server has a "context path" that is "/servlet/caldav". The + client will use "/.well-known/caldav" as the path for its + "bootstrapping" process after it has first found the FQDN and port + number via an SRV lookup or via manual entry of information by the + user, from which the client can parse suitable information. When the + client makes an HTTP request against "/.well-known/caldav", the + server would issue an HTTP redirect response with a Location response + header using the path "/servlet/caldav". The client would then + "follow" this redirect to the new resource and continue making HTTP + requests there to complete its "bootstrapping" process. + +6. Client "Bootstrapping" Procedures + + This section describes a procedure that CalDAV or CardDAV clients + SHOULD use to do their initial configuration based on minimal user + input. The goal is to determine an http: or https: URI that + describes the full path to the user's principal-URL [RFC3744]. + + 1. Processing user input: + + * For a CalDAV server: + + + Minimal input from a user would consist of a calendar user + address and a password. A calendar user address is defined + by iCalendar [RFC5545] to be a URI [RFC3986]. Provided a + user identifier and a domain name can be extracted from the + URI, this simple "bootstrapping" configuration can be done. + + + If the calendar user address is a "mailto:" [RFC6068] URI, + the "mailbox" portion of the URI is examined, and the + "local-part" and "domain" portions are extracted. + + + If the calendar user address is an "http:" [RFC2616] or + "https:" [RFC2818] URI, the "userinfo" and "host" portion + of the URI [RFC3986] is extracted. + + + + +Daboo Standards Track [Page 5] + +RFC 6764 SRV for CalDAV & CardDAV February 2013 + + + * For a CardDAV server: + + + Minimal input from a user would consist of their email + address [RFC5322] for the domain where the CardDAV service + is hosted, and a password. The "mailbox" portion of the + email address is examined, and the "local-part" and + "domain" portions are extracted. + + 2. Determination of service FQDN and port number: + + * An SRV lookup for _caldavs._tcp (for CalDAV) or _carddavs._tcp + (for CardDAV) is done with the extracted "domain" as the + service domain. + + * If no result is found, the client can try _caldav._tcp (for + CalDAV) or _carddav._tcp (for CardDAV) provided non-TLS + connections are appropriate. + + * If an SRV record is returned, the client extracts the target + FQDN and port number. If multiple SRV records are returned, + the client MUST use the Priority and Weight fields in the + record to determine which one to pick (as per [RFC2782]). + + * If an SRV record is not found, the client will need to prompt + the user to enter the FQDN and port number information + directly or use some other heuristic, for example, using the + extracted "domain" as the FQDN and default HTTPS or HTTP port + numbers. In this situation, clients MUST first attempt an + HTTP connection with TLS. + + 3. Determination of initial "context path": + + * When an SRV lookup is done and a valid SRV record returned, + the client MUST also query for a corresponding TXT record and + check for the presence of a "path" key in its response. If + present, the value of the "path" key is used for the initial + "context path". + + * When an initial "context path" has not been determined from a + TXT record, the initial "context path" is taken to be + "/.well-known/caldav" (for CalDAV) or "/.well-known/carddav" + (for CardDAV). + + * If the initial "context path" derived from a TXT record + generates HTTP errors when targeted by requests, the client + SHOULD repeat its "bootstrapping" procedure using the + appropriate ".well-known" URI instead. + + + + +Daboo Standards Track [Page 6] + +RFC 6764 SRV for CalDAV & CardDAV February 2013 + + + 4. Determination of user identifier: + + * The client will need to make authenticated HTTP requests to + the service. Typically, a "user identifier" is required for + some form of user/password authentication. When a user + identifier is required, clients MUST first use the "mailbox" + portion of the calendar user address provided by the user in + the case of a "mailto:" address and, if that results in an + authentication failure, SHOULD fall back to using the "local- + part" extracted from the "mailto:" address. For an "http:" or + "https:" calendar user address, the "userinfo" portion is used + as the user identifier for authentication. This is in line + with the guidance outlined in Section 7. If these user + identifiers result in authentication failure, the client + SHOULD prompt the user for a valid identifier. + + 5. Connecting to the service: + + * Subsequent to configuration, the client will make HTTP + requests to the service. When using "_caldavs" or "_carddavs" + services, a TLS negotiation is done immediately upon + connection. The client MUST do certificate verification using + the procedure outlined in Section 6 of [RFC6125] in regard to + verification with an SRV RR as the starting point. + + * The client does a "PROPFIND" [RFC4918] request with the + request URI set to the initial "context path". The body of + the request SHOULD include the DAV:current-user-principal + [RFC5397] property as one of the properties to return. Note + that clients MUST properly handle HTTP redirect responses for + the request. The server will use the HTTP authentication + procedure outlined in [RFC2617] or use some other appropriate + authentication schemes to authenticate the user. + + * If the server returns a 404 ("Not Found") HTTP status response + to the request on the initial "context path", clients MAY try + repeating the request on the "root" URI "/" or prompt the user + for a suitable path. + + * If the DAV:current-user-principal property is returned on the + request, the client uses that value for the principal-URL of + the authenticated user. With that, it can execute a + "PROPFIND" request on the principal-URL and discover + additional properties for configuration (e.g., calendar or + address book "home" collections). + + + + + + +Daboo Standards Track [Page 7] + +RFC 6764 SRV for CalDAV & CardDAV February 2013 + + + * If the DAV:current-user-principal property is not returned, + then the client will need to request the principal-URL path + from the user in order to continue with configuration. + + Once a successful account discovery step has been done, clients + SHOULD cache the service details that were successfully used (user + identity, principal-URL with full scheme/host/port details) and reuse + those when connecting again at a later time. + + If a subsequent connection attempt fails, or authentication fails + persistently, clients SHOULD retry the SRV lookup and account + discovery to "refresh" the cached data. + +7. Guidance for Service Providers + + Service providers wanting to offer CalDAV or CardDAV services that + can be configured by clients using SRV records need to follow certain + procedures to ensure proper operation. + + o CalDAV or CardDAV servers SHOULD be configured to allow + authentication with calendar user addresses (just taking the + "mailbox" portion of any "mailto:" URI) or email addresses + respectively, or with "user identifiers" extracted from them. In + the former case, the addresses MUST NOT conflict with other forms + of a permitted user login name. In the latter case, the extracted + "user identifiers" need to be unique across the server and MUST + NOT conflict with any login name on the server. + + o Servers MUST force authentication for "PROPFIND" requests that + retrieve the DAV:current-user-principal property to ensure that + the value of the DAV:current-user-principal property returned + corresponds to the principal-URL of the user making the request. + + o If the service provider uses TLS, the service provider MUST ensure + a certificate is installed that can be verified by clients using + the procedure outlined in Section 6 of [RFC6125] in regard to + verification with an SRV RR as the starting point. In particular, + certificates SHOULD include SRV-ID and DNS-ID identifiers as + appropriate, as described in Section 8. + + o Service providers should install the appropriate SRV records for + the offered services and optionally include TXT records. + + + + + + + + + +Daboo Standards Track [Page 8] + +RFC 6764 SRV for CalDAV & CardDAV February 2013 + + +8. Security Considerations + + Clients that support TLS as defined by [RFC2818] SHOULD try the + "_caldavs" or "_carddavs" services first before trying the "_caldav" + or "_carddav" services respectively. If a user has explicitly + requested a connection with TLS, the client MUST NOT use any service + information returned for the "_caldav" or "_carddav" services. + Clients MUST follow the certificate-verification process specified in + [RFC6125]. + + A malicious attacker with access to the DNS server data, or that is + able to get spoofed answers cached in a recursive resolver, can + potentially cause clients to connect to any server chosen by the + attacker. In the absence of a secure DNS option, clients SHOULD + check that the target FQDN returned in the SRV record matches the + original service domain that was queried. If the target FQDN is not + in the queried domain, clients SHOULD verify with the user that the + SRV target FQDN is suitable for use before executing any connections + to the host. Alternatively, if TLS is being used for the service, + clients MUST use the procedure outlined in Section 6 of [RFC6125] to + verify the service. When the target FQDN does not match the original + service domain that was queried, clients MUST check the SRV-ID + identifier in the server's certificate. If the FQDN does match, + clients MUST check any SRV-ID identifiers in the server's certificate + or, if no SRV-ID identifiers are present, MUST check the DNS-ID + identifiers in the server's certificate. + + Implementations of TLS [RFC5246], used as the basis for TLS + ([RFC2818]), typically support multiple versions of the protocol as + well as the older SSL (Secure Sockets Layer) protocol. Because of + known security vulnerabilities, clients and servers MUST NOT request, + offer, or use SSL 2.0. See Appendix E.2 of [RFC5246] for further + details. + +9. IANA Considerations + +9.1. Well-Known URI Registrations + + This document defines two ".well-known" URIs using the registration + procedure and template from Section 5.1 of [RFC5785]. + + + + + + + + + + + +Daboo Standards Track [Page 9] + +RFC 6764 SRV for CalDAV & CardDAV February 2013 + + +9.1.1. caldav Well-Known URI Registration + + URI suffix: caldav + + Change controller: IETF + + Specification document(s): This RFC + + Related information: See also [RFC4791]. + +9.1.2. carddav Well-Known URI Registration + + URI suffix: carddav + + Change controller: IETF + + Specification document(s): This RFC + + Related information: See also [RFC6352]. + +9.2. Service Name Registrations + + This document registers four new service names as per [RFC6335]. Two + are defined in this document, and two are defined in [RFC6352], + Section 11. + +9.2.1. caldav Service Name Registration + + Service Name: caldav + + Transport Protocol(s): TCP + + Assignee: IESG + + Contact: IETF Chair + + Description: Calendaring Extensions to WebDAV (CalDAV) - non-TLS + + Reference: [RFC6764] + + Assignment Note: This is an extension of the http service. Defined + TXT keys: path= + + + + + + + + + +Daboo Standards Track [Page 10] + +RFC 6764 SRV for CalDAV & CardDAV February 2013 + + +9.2.2. caldavs Service Name Registration + + Service Name: caldavs + + Transport Protocol(s): TCP + + Assignee: IESG + + Contact: IETF Chair + + Description: Calendaring Extensions to WebDAV (CalDAV) - over TLS + + Reference: [RFC6764] + + Assignment Note: This is an extension of the https service. Defined + TXT keys: path= + +9.2.3. carddav Service Name Registration + + Service Name: carddav + + Transport Protocol(s): TCP + + Assignee: IESG + + Contact: IETF Chair + + Description: vCard Extensions to WebDAV (CardDAV) - non-TLS + + Reference: [RFC6352] + + Assignment Note: This is an extension of the http service. Defined + TXT keys: path= + + + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 11] + +RFC 6764 SRV for CalDAV & CardDAV February 2013 + + +9.2.4. carddavs Service Name Registration + + Service Name: carddavs + + Transport Protocol(s): TCP + + Assignee: IESG + + Contact: IETF Chair + + Description: vCard Extensions to WebDAV (CardDAV) - over TLS + + Reference: [RFC6352] + + Assignment Note: This is an extension of the https service. Defined + TXT keys: path= + +10. Acknowledgments + + This specification was suggested by discussion that took place within + the Calendaring and Scheduling Consortium's CalDAV Technical + Committee. The author thanks the following for their contributions: + Stuart Cheshire, Bernard Desruisseaux, Eran Hammer-Lahav, Helge Hess, + Arnaud Quillaud, Wilfredo Sanchez, and Joe Touch. + +11. References + +11.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + + [RFC2617] Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., + Leach, P., Luotonen, A., and L. Stewart, "HTTP + Authentication: Basic and Digest Access Authentication", + RFC 2617, June 1999. + + [RFC2782] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR for + specifying the location of services (DNS SRV)", RFC 2782, + February 2000. + + [RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, May 2000. + + + + + +Daboo Standards Track [Page 12] + +RFC 6764 SRV for CalDAV & CardDAV February 2013 + + + [RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. Whitehead, "Web + Distributed Authoring and Versioning (WebDAV) + Access Control Protocol", RFC 3744, May 2004. + + [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform + Resource Identifier (URI): Generic Syntax", STD 66, + RFC 3986, January 2005. + + [RFC4791] Daboo, C., Desruisseaux, B., and L. Dusseault, + "Calendaring Extensions to WebDAV (CalDAV)", RFC 4791, + March 2007. + + [RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed + Authoring and Versioning (WebDAV)", RFC 4918, June 2007. + + [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security + (TLS) Protocol Version 1.2", RFC 5246, August 2008. + + [RFC5322] Resnick, P., Ed., "Internet Message Format", RFC 5322, + October 2008. + + [RFC5397] Sanchez, W. and C. Daboo, "WebDAV Current Principal + Extension", RFC 5397, December 2008. + + [RFC5785] Nottingham, M. and E. Hammer-Lahav, "Defining Well-Known + Uniform Resource Identifiers (URIs)", RFC 5785, + April 2010. + + [RFC6068] Duerst, M., Masinter, L., and J. Zawinski, "The 'mailto' + URI Scheme", RFC 6068, October 2010. + + [RFC6125] Saint-Andre, P. and J. Hodges, "Representation and + Verification of Domain-Based Application Service Identity + within Internet Public Key Infrastructure Using X.509 + (PKIX) Certificates in the Context of Transport Layer + Security (TLS)", RFC 6125, March 2011. + + [RFC6335] Cotton, M., Eggert, L., Touch, J., Westerlund, M., and S. + Cheshire, "Internet Assigned Numbers Authority (IANA) + Procedures for the Management of the Service Name and + Transport Protocol Port Number Registry", BCP 165, + RFC 6335, August 2011. + + [RFC6352] Daboo, C., "CardDAV: vCard Extensions to Web Distributed + Authoring and Versioning (WebDAV)", RFC 6352, August 2011. + + [RFC6763] Cheshire, S. and M. Krochmal, "DNS-Based Service + Discovery", RFC 6763, February 2013. + + + +Daboo Standards Track [Page 13] + +RFC 6764 SRV for CalDAV & CardDAV February 2013 + + +11.2. Informative References + + [RFC5545] Desruisseaux, B., "Internet Calendaring and Scheduling + Core Object Specification (iCalendar)", RFC 5545, + September 2009. + +Author's Address + + Cyrus Daboo + Apple Inc. + 1 Infinite Loop + Cupertino, CA 95014 + USA + + EMail: cyrus@daboo.name + URI: http://www.apple.com/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Daboo Standards Track [Page 14] + diff --git a/eclipse-libs/lombok-api.jar b/eclipse-libs/lombok-api.jar new file mode 100644 index 00000000..db3dff79 Binary files /dev/null and b/eclipse-libs/lombok-api.jar differ diff --git a/libs/backport-util-concurrent-3.1.jar b/libs/backport-util-concurrent-3.1.jar new file mode 100644 index 00000000..3a4c2797 Binary files /dev/null and b/libs/backport-util-concurrent-3.1.jar differ diff --git a/libs/commons-codec-1.8.jar b/libs/commons-codec-1.8.jar new file mode 100644 index 00000000..32f84c92 Binary files /dev/null and b/libs/commons-codec-1.8.jar differ diff --git a/libs/commons-io-2.4.jar b/libs/commons-io-2.4.jar new file mode 100644 index 00000000..90035a4f Binary files /dev/null and b/libs/commons-io-2.4.jar differ diff --git a/libs/commons-lang-2.6.jar b/libs/commons-lang-2.6.jar new file mode 100644 index 00000000..98467d3a Binary files /dev/null and b/libs/commons-lang-2.6.jar differ diff --git a/libs/commons-logging-1.1.3.jar b/libs/commons-logging-1.1.3.jar new file mode 100644 index 00000000..ab512540 Binary files /dev/null and b/libs/commons-logging-1.1.3.jar differ diff --git a/libs/ez-vcard-0.9.6.jar b/libs/ez-vcard-0.9.6.jar new file mode 100644 index 00000000..0bb07c9d Binary files /dev/null and b/libs/ez-vcard-0.9.6.jar differ diff --git a/libs/httpclientandroidlib-1.2.1.jar b/libs/httpclientandroidlib-1.2.1.jar new file mode 100644 index 00000000..f409b0b2 Binary files /dev/null and b/libs/httpclientandroidlib-1.2.1.jar differ diff --git a/libs/ical4j-1.0.6-davdroid141027.jar b/libs/ical4j-1.0.6-davdroid141027.jar new file mode 100644 index 00000000..6a6ec1bf Binary files /dev/null and b/libs/ical4j-1.0.6-davdroid141027.jar differ diff --git a/libs/org.xbill.dns_2.1.6.jar b/libs/org.xbill.dns_2.1.6.jar new file mode 100644 index 00000000..3d42fbc4 Binary files /dev/null and b/libs/org.xbill.dns_2.1.6.jar differ diff --git a/libs/simple-xml-2.7.jar b/libs/simple-xml-2.7.jar new file mode 100644 index 00000000..cb12687a Binary files /dev/null and b/libs/simple-xml-2.7.jar differ diff --git a/proguard-project.txt b/proguard-project.txt new file mode 100644 index 00000000..7c96635c --- /dev/null +++ b/proguard-project.txt @@ -0,0 +1,28 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclassmembers + +-dontwarn edu.emory.mathcs.backport.** +-dontwarn ezvcard.** +-dontwarn net.fortuna.** +-dontwarn org.apache.** +-dontwarn org.simpleframework.xml.** + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/project.properties b/project.properties new file mode 100644 index 00000000..6e18427a --- /dev/null +++ b/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-21 diff --git a/res/drawable-hdpi/addressbook.png b/res/drawable-hdpi/addressbook.png new file mode 100644 index 00000000..79655934 Binary files /dev/null and b/res/drawable-hdpi/addressbook.png differ diff --git a/res/drawable-hdpi/alerts_and_states_warning.png b/res/drawable-hdpi/alerts_and_states_warning.png new file mode 100644 index 00000000..a82ac4ac Binary files /dev/null and b/res/drawable-hdpi/alerts_and_states_warning.png differ diff --git a/res/drawable-hdpi/calendar.png b/res/drawable-hdpi/calendar.png new file mode 100644 index 00000000..3b5f92c3 Binary files /dev/null and b/res/drawable-hdpi/calendar.png differ diff --git a/res/drawable-hdpi/extra_actions_about.png b/res/drawable-hdpi/extra_actions_about.png new file mode 100644 index 00000000..e5582f4c Binary files /dev/null and b/res/drawable-hdpi/extra_actions_about.png differ diff --git a/res/drawable-hdpi/ic_action_new_account.png b/res/drawable-hdpi/ic_action_new_account.png new file mode 100644 index 00000000..9a41829f Binary files /dev/null and b/res/drawable-hdpi/ic_action_new_account.png differ diff --git a/res/drawable-hdpi/ic_action_new_event.png b/res/drawable-hdpi/ic_action_new_event.png new file mode 100644 index 00000000..5a5d5587 Binary files /dev/null and b/res/drawable-hdpi/ic_action_new_event.png differ diff --git a/res/drawable-hdpi/ic_launcher.png b/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 00000000..dce11e1c Binary files /dev/null and b/res/drawable-hdpi/ic_launcher.png differ diff --git a/res/drawable-hdpi/ic_read_only.png b/res/drawable-hdpi/ic_read_only.png new file mode 100644 index 00000000..aca46301 Binary files /dev/null and b/res/drawable-hdpi/ic_read_only.png differ diff --git a/res/drawable-hdpi/navigation_accept.png b/res/drawable-hdpi/navigation_accept.png new file mode 100644 index 00000000..58bf9721 Binary files /dev/null and b/res/drawable-hdpi/navigation_accept.png differ diff --git a/res/drawable-hdpi/navigation_forward.png b/res/drawable-hdpi/navigation_forward.png new file mode 100644 index 00000000..812b3aaf Binary files /dev/null and b/res/drawable-hdpi/navigation_forward.png differ diff --git a/res/drawable-hdpi/show_sync_settings.png b/res/drawable-hdpi/show_sync_settings.png new file mode 100644 index 00000000..4a6fb5b5 Binary files /dev/null and b/res/drawable-hdpi/show_sync_settings.png differ diff --git a/res/drawable-hdpi/view_website.png b/res/drawable-hdpi/view_website.png new file mode 100644 index 00000000..9edbb1b5 Binary files /dev/null and b/res/drawable-hdpi/view_website.png differ diff --git a/res/drawable-mdpi/addressbook.png b/res/drawable-mdpi/addressbook.png new file mode 100644 index 00000000..4779b7dd Binary files /dev/null and b/res/drawable-mdpi/addressbook.png differ diff --git a/res/drawable-mdpi/alerts_and_states_warning.png b/res/drawable-mdpi/alerts_and_states_warning.png new file mode 100644 index 00000000..f620ca52 Binary files /dev/null and b/res/drawable-mdpi/alerts_and_states_warning.png differ diff --git a/res/drawable-mdpi/calendar.png b/res/drawable-mdpi/calendar.png new file mode 100644 index 00000000..1bb6a071 Binary files /dev/null and b/res/drawable-mdpi/calendar.png differ diff --git a/res/drawable-mdpi/extra_actions_about.png b/res/drawable-mdpi/extra_actions_about.png new file mode 100644 index 00000000..25d68263 Binary files /dev/null and b/res/drawable-mdpi/extra_actions_about.png differ diff --git a/res/drawable-mdpi/ic_action_new_account.png b/res/drawable-mdpi/ic_action_new_account.png new file mode 100644 index 00000000..10e17d08 Binary files /dev/null and b/res/drawable-mdpi/ic_action_new_account.png differ diff --git a/res/drawable-mdpi/ic_action_new_event.png b/res/drawable-mdpi/ic_action_new_event.png new file mode 100644 index 00000000..e8b7e41a Binary files /dev/null and b/res/drawable-mdpi/ic_action_new_event.png differ diff --git a/res/drawable-mdpi/ic_launcher.png b/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 00000000..d4520816 Binary files /dev/null and b/res/drawable-mdpi/ic_launcher.png differ diff --git a/res/drawable-mdpi/ic_read_only.png b/res/drawable-mdpi/ic_read_only.png new file mode 100644 index 00000000..b2434f76 Binary files /dev/null and b/res/drawable-mdpi/ic_read_only.png differ diff --git a/res/drawable-mdpi/navigation_accept.png b/res/drawable-mdpi/navigation_accept.png new file mode 100644 index 00000000..cf5fab3a Binary files /dev/null and b/res/drawable-mdpi/navigation_accept.png differ diff --git a/res/drawable-mdpi/navigation_forward.png b/res/drawable-mdpi/navigation_forward.png new file mode 100644 index 00000000..75700a7f Binary files /dev/null and b/res/drawable-mdpi/navigation_forward.png differ diff --git a/res/drawable-mdpi/show_sync_settings.png b/res/drawable-mdpi/show_sync_settings.png new file mode 100644 index 00000000..9e7a7756 Binary files /dev/null and b/res/drawable-mdpi/show_sync_settings.png differ diff --git a/res/drawable-mdpi/view_website.png b/res/drawable-mdpi/view_website.png new file mode 100644 index 00000000..7a27476b Binary files /dev/null and b/res/drawable-mdpi/view_website.png differ diff --git a/res/drawable-xhdpi/addressbook.png b/res/drawable-xhdpi/addressbook.png new file mode 100644 index 00000000..3090e596 Binary files /dev/null and b/res/drawable-xhdpi/addressbook.png differ diff --git a/res/drawable-xhdpi/alerts_and_states_warning.png b/res/drawable-xhdpi/alerts_and_states_warning.png new file mode 100644 index 00000000..126fc78e Binary files /dev/null and b/res/drawable-xhdpi/alerts_and_states_warning.png differ diff --git a/res/drawable-xhdpi/calendar.png b/res/drawable-xhdpi/calendar.png new file mode 100644 index 00000000..5fd726a7 Binary files /dev/null and b/res/drawable-xhdpi/calendar.png differ diff --git a/res/drawable-xhdpi/extra_actions_about.png b/res/drawable-xhdpi/extra_actions_about.png new file mode 100644 index 00000000..69cacb79 Binary files /dev/null and b/res/drawable-xhdpi/extra_actions_about.png differ diff --git a/res/drawable-xhdpi/ic_action_new_account.png b/res/drawable-xhdpi/ic_action_new_account.png new file mode 100644 index 00000000..addee18c Binary files /dev/null and b/res/drawable-xhdpi/ic_action_new_account.png differ diff --git a/res/drawable-xhdpi/ic_action_new_event.png b/res/drawable-xhdpi/ic_action_new_event.png new file mode 100644 index 00000000..f2435cb3 Binary files /dev/null and b/res/drawable-xhdpi/ic_action_new_event.png differ diff --git a/res/drawable-xhdpi/ic_launcher.png b/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 00000000..e4b9000a Binary files /dev/null and b/res/drawable-xhdpi/ic_launcher.png differ diff --git a/res/drawable-xhdpi/ic_read_only.png b/res/drawable-xhdpi/ic_read_only.png new file mode 100644 index 00000000..43d96617 Binary files /dev/null and b/res/drawable-xhdpi/ic_read_only.png differ diff --git a/res/drawable-xhdpi/navigation_accept.png b/res/drawable-xhdpi/navigation_accept.png new file mode 100644 index 00000000..b8915716 Binary files /dev/null and b/res/drawable-xhdpi/navigation_accept.png differ diff --git a/res/drawable-xhdpi/navigation_forward.png b/res/drawable-xhdpi/navigation_forward.png new file mode 100644 index 00000000..1f60fc63 Binary files /dev/null and b/res/drawable-xhdpi/navigation_forward.png differ diff --git a/res/drawable-xhdpi/show_sync_settings.png b/res/drawable-xhdpi/show_sync_settings.png new file mode 100644 index 00000000..64bcc5f7 Binary files /dev/null and b/res/drawable-xhdpi/show_sync_settings.png differ diff --git a/res/drawable-xhdpi/view_website.png b/res/drawable-xhdpi/view_website.png new file mode 100644 index 00000000..aba86f0b Binary files /dev/null and b/res/drawable-xhdpi/view_website.png differ diff --git a/res/drawable-xxhdpi/addressbook.png b/res/drawable-xxhdpi/addressbook.png new file mode 100644 index 00000000..c23e339e Binary files /dev/null and b/res/drawable-xxhdpi/addressbook.png differ diff --git a/res/drawable-xxhdpi/alerts_and_states_warning.png b/res/drawable-xxhdpi/alerts_and_states_warning.png new file mode 100644 index 00000000..c858e047 Binary files /dev/null and b/res/drawable-xxhdpi/alerts_and_states_warning.png differ diff --git a/res/drawable-xxhdpi/calendar.png b/res/drawable-xxhdpi/calendar.png new file mode 100644 index 00000000..e604ef5d Binary files /dev/null and b/res/drawable-xxhdpi/calendar.png differ diff --git a/res/drawable-xxhdpi/extra_actions_about.png b/res/drawable-xxhdpi/extra_actions_about.png new file mode 100644 index 00000000..c01120fd Binary files /dev/null and b/res/drawable-xxhdpi/extra_actions_about.png differ diff --git a/res/drawable-xxhdpi/ic_action_new_account.png b/res/drawable-xxhdpi/ic_action_new_account.png new file mode 100644 index 00000000..53c08a8c Binary files /dev/null and b/res/drawable-xxhdpi/ic_action_new_account.png differ diff --git a/res/drawable-xxhdpi/ic_action_new_event.png b/res/drawable-xxhdpi/ic_action_new_event.png new file mode 100644 index 00000000..ebd24107 Binary files /dev/null and b/res/drawable-xxhdpi/ic_action_new_event.png differ diff --git a/res/drawable-xxhdpi/ic_launcher.png b/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..126ae37e Binary files /dev/null and b/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/res/drawable-xxhdpi/ic_read_only.png b/res/drawable-xxhdpi/ic_read_only.png new file mode 100644 index 00000000..6ce79f86 Binary files /dev/null and b/res/drawable-xxhdpi/ic_read_only.png differ diff --git a/res/drawable-xxhdpi/navigation_accept.png b/res/drawable-xxhdpi/navigation_accept.png new file mode 100644 index 00000000..6fda89ec Binary files /dev/null and b/res/drawable-xxhdpi/navigation_accept.png differ diff --git a/res/drawable-xxhdpi/navigation_forward.png b/res/drawable-xxhdpi/navigation_forward.png new file mode 100644 index 00000000..a4e6f382 Binary files /dev/null and b/res/drawable-xxhdpi/navigation_forward.png differ diff --git a/res/drawable-xxhdpi/show_sync_settings.png b/res/drawable-xxhdpi/show_sync_settings.png new file mode 100644 index 00000000..209f8917 Binary files /dev/null and b/res/drawable-xxhdpi/show_sync_settings.png differ diff --git a/res/drawable-xxhdpi/view_website.png b/res/drawable-xxhdpi/view_website.png new file mode 100644 index 00000000..05de707e Binary files /dev/null and b/res/drawable-xxhdpi/view_website.png differ diff --git a/res/layout/account_details.xml b/res/layout/account_details.xml new file mode 100644 index 00000000..40790aba --- /dev/null +++ b/res/layout/account_details.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/activity_main.xml b/res/layout/activity_main.xml new file mode 100644 index 00000000..1337c4e4 --- /dev/null +++ b/res/layout/activity_main.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/add_account.xml b/res/layout/add_account.xml new file mode 100644 index 00000000..5bf370dd --- /dev/null +++ b/res/layout/add_account.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/res/layout/address_books_heading.xml b/res/layout/address_books_heading.xml new file mode 100644 index 00000000..f97ecb90 --- /dev/null +++ b/res/layout/address_books_heading.xml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/res/layout/calendars_heading.xml b/res/layout/calendars_heading.xml new file mode 100644 index 00000000..770eb099 --- /dev/null +++ b/res/layout/calendars_heading.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/res/layout/login_email.xml b/res/layout/login_email.xml new file mode 100644 index 00000000..8e5a0a78 --- /dev/null +++ b/res/layout/login_email.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + diff --git a/res/layout/login_type.xml b/res/layout/login_type.xml new file mode 100644 index 00000000..8ce0277e --- /dev/null +++ b/res/layout/login_type.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + diff --git a/res/layout/login_url.xml b/res/layout/login_url.xml new file mode 100644 index 00000000..a712b08f --- /dev/null +++ b/res/layout/login_url.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/query_server.xml b/res/layout/query_server.xml new file mode 100644 index 00000000..5e3f83eb --- /dev/null +++ b/res/layout/query_server.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/res/layout/select_collections_header.xml b/res/layout/select_collections_header.xml new file mode 100644 index 00000000..521dc124 --- /dev/null +++ b/res/layout/select_collections_header.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/res/menu/account_details.xml b/res/menu/account_details.xml new file mode 100644 index 00000000..e3bfc0b9 --- /dev/null +++ b/res/menu/account_details.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/res/menu/add_account.xml b/res/menu/add_account.xml new file mode 100644 index 00000000..6892f08d --- /dev/null +++ b/res/menu/add_account.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/res/menu/debug_settings.xml b/res/menu/debug_settings.xml new file mode 100644 index 00000000..40f8ba75 --- /dev/null +++ b/res/menu/debug_settings.xml @@ -0,0 +1,4 @@ + + + + diff --git a/res/menu/main_activity.xml b/res/menu/main_activity.xml new file mode 100644 index 00000000..132a9981 --- /dev/null +++ b/res/menu/main_activity.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/res/menu/only_next.xml b/res/menu/only_next.xml new file mode 100644 index 00000000..bd075ce5 --- /dev/null +++ b/res/menu/only_next.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml new file mode 100644 index 00000000..64132a6c --- /dev/null +++ b/res/values-ca/strings.xml @@ -0,0 +1,142 @@ + + + + + DAVdroid + + Lloc web de DAVdroid + Següent + Ajuda + + Error HTTP: %s + Falten capacitats: %s + Error E/S: %s + URI invàlida: %s + + + Gestioneu els comptes a sincronitzar + Gràcies per comprar DAVDroid a travès de Google Play i així donar suport al projecte. De totes formes, hi ha dos incidències amb el Google Play:

+ +

1. Les comptes poden desaparèixer desprès de reiniciar

+

Podeu trobar-vos amb el problema de que totes les vostres comptes de DAVdroid (contactes i events inclosos) han desaparegut + desprès de reiniciar el dispositiu. La raó és un error de Android + que causa que totes les comptes de les aplicacións de pagament s\'eliminen al iniciar perquè els fitxers (encriptats) APK es + carreguen desprès de revisar si existeixen comptes orfanes.

+

Usuaris afectats:
+ * Tots els usuaris de Android 4.1 que han obtingut el DAVdroid del Play Store;
+ * El usuaris de Android 4.2 que han obtingut el DAVdroid del Play Store només en alguns dispositius (per exemple, la majoria de dispositius Samsung)

+ +

2. Els comptes poden desaparèixer desprès d\'actualitar el DAVdroid

+

Podeu trobar-vos amb el problema de que totes les vostres comptes de DAVdroid (contactes i events inclosos) han desaparegut quan el Play Store + actualitza el DAVdroid. La raó és unaltre error de Android + que causa que les comptes del les aplicacións de pagament s\'esborrin al actualitzar per alguna raó similar.

+

Usuaris afectats: alguns de Android 4.4.2 que han obtingut el DAVdroid desde el Play Store (es coneix per dispositius Nexus i Moto G)

+ +

Si esteu afectats per algun d\'aquests errors, sisuplau instaleu el + DAVdroid JB Workaround.

+ ]]> + Benvingut a DAVdroid/%s! + +

DAVdroid es un adaptador de Android 4+ per la sincronització de CalDAV/CardDAV. Per a utilitzar-lo afegieu una compte de DAVdroid + pel vostre servidor de CalDAV/CardDAV i els vostres contactes/esdeveiments es sincronitzaràn de forma bidireccional.

+ +

Per mes informació, podeu visitar la pàgina web de DAVdroid. + També hi ha una guia de configuració. El DAVdroid respecta + la teva privacitat, feu una ullada a la nostra politica de privacitat.

+ +

Si feu servir CyanogenMod, "Privacy Guard" ha d\'estar deshabilitat pel DAVdroid. Si no es així, el DAVdroid no pot accedir ni sincronitzar + els vostres contactes i events.

+ +

En cas de problemes, sisplau llegiu el FAQ primer. + Si trobeu una errada que està clarament relacionada amb el DAVdroid, afegiu-la directament a + Github en comptes de contactar-nos directament o de donar una + valoració pobra de l\'aplicació.

+ +

Codi Obert

+

DAVdroid està disenyat per a ser un proejcte de codi obert desde bon principi. Sempres es possible compilar l\'aplicació per la vostra part + i utilitzar-la gratuitament sense cap obligació. El codi font es troba + app yourself and use it for free without any obligations. The source code is + disponible a Github, i també podeu + descarregar l\'aplicació al F-droid.

+ +

De totes formes, es va fer molta feina per crear aquesta applicació, així que vam decidir ficar-la a les tendes comercials per un petit import. + Si voleu suportar aquest project, siusplau considereu fer una donació a DAVdroid + o comprar-lo.

+ +

Llicència

+

Copyright (c) 2013 – 2014 Ricki Hirner (bitfire web engineering). Tots els drets reservats. + Aquest programa i tots els materials que l\'acompanyen estan disponibles sota els termes de la GNU Public License v3.0 que acompanya + aquesta distribució i està disponible a http://www.gnu.org/licenses/gpl.html. Respecte al Google Play, Samsung + Store, AndroidPit App Center o Amazon Appstore que requereixen altres condicions, els termes respectius apliquen per les versions + que s\'han descàrregat a travès d\'aquests serveis.

+ +

La traducció alemana es realitzada pels mateixos autors. Les traduccions a altres idiomes han estat contribucións de varies persones que es mencionen en el seu idioma específic. +

+

Traducció: + Català: Sergi Almacellas Abellana +

+ +

Llibraries de tercers utilitzades

+

+ * iCal4j (New BSD License)
+ * ez-vcard (New BSD License)
+ * Simple XML Serialization (Apache License, Version 2.0)
+ * Project Lombok (MIT License)

+ * dnsjavaBSD License

+ ]]>
+ + + Entra amb una adreça de correu electrònic + Els detalls del servei es detectaran automàticament amb el nom del servidor. Per exemple: elmeucompte@icloud.com + Entra amb una URL i un nom d\'usuari + Els detalls del servei es detectaran de forma automàtica amb al URL inicial i el nom d\'usuari. Normalment utilitzat per servers allotjats en servidors propis. + + Si us plau, introduïu la vostra adreça de correu electrònic. El seu nom de domini s\'utiltizara per auto-detectar la configuració dels serveis. + Correu electrònic: + + http:// + https:// + + + "Si no feu servir cap encriptació (HTTPS), altres usuaris poden interceptar fàcilment les vostres credencials, contactes i events." + Nom d\'usuari: + URL Arrel (les coleccions es detecten automàticament): + Autentificació preferent (recomanat però incompatible amb l\'autentificació Digest) + Contrasenya: + DAVdroid: Seleccioneu col·leccions + Ni CalDAV ni CardDAV estan disponibles a aquesta ubicació. + + Afegir compte + Contactant servidor. Espereu sisuplau. + Quines col·leccions s\'han de sincronitzar? + Llibretes de contactes + Llibreta de contactes + Calendaris + Calendari + Seleccioneu com a molt una llibreta de contactes (Polseu de nou per deseleccionar): + Seleccioneu els vostres calendaris: + + Detalls del compte + Nom del compte: + El meu compte CalDAV/CardDAV + Correu electrònic: + "ORGANITZADOR dels teus events. Es requereix si feu servir la informació dels assitents" + "Podeu fer servir la vostra addreça de correu electrònic com a nom de la compta ja que el Android utiltizar el nom de la compta com a camp ORGANITZADOR pels events que vosaltres creeu. No podeu tenir dos comptes amb el mateix nom. + només-lectura + + + Configuració general + Configuració deputarció + Desactiva compressió HTTP + La compressió HTTP està desactivada (mode depuració) + La compressió HTTP s\'utilitza quan es possible. + Registra el tràfic de xarxa + Tot el tràfic de xarxa es reigstra (mode depuració) + No es registra el tràfic de xarxa + Informa d\'un error + + + diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml new file mode 100644 index 00000000..c74d42dc --- /dev/null +++ b/res/values-cs/strings.xml @@ -0,0 +1,141 @@ + + + + + DAVdroid + + Webová stránka DAVdroid + Další + Pomoc + + HTTP chyba: %s + Chybějící možnosti: %s + I/O chyba: %s + Neplatné URI: %s + + + Spravovat synchronizované účty + + Thank you for buying DAVdroid via Google Play and thus supporting the project. Unfortunately, there are two issues with Google Play:

+ +

1. Accounts may be gone after a reboot

+

You may encounter the problem that all your DAVdroid accounts (including contacts and events) are gone + after rebooting your device. The reason is a bug in Android + that causes accounts of paid apps to be removed on start-up because the (encrypted) APK files are + loaded after checking for orphaned accounts.

+

Affected users:
+ * all Android 4.1 users who have got DAVdroid from Play Store;
+ * Android 4.2 users who have got DAVdroid from Play Store only with certain devices (for instance, most Samsung devices)

+ +

2. Accounts may be gone after upgrading DAVdroid

+

You may encounter the problem that all your DAVdroid accounts (including contacts and events) when Play Store + updates DAVdroid. The reason is another bug in Android + that causes accounts of paid apps to be removed when upgrading for a similar reason.

+

Affected users: some Android 4.4.2 users who have got DAVdroid from Play Store (known for Nexus devices and Moto G)

+ +

If you\'re affected by one of these bugs, please install the + DAVdroid JB Workaround.

+ ]]>
+ Vítejte do aplikace DAVdroid/%s! + +

DAVdroid je aplikace pro Android 4+ na synchronizaci CalDAV/CardDAV účtů. Stačí přidat účet DAVdroid + pro CalDAV/CardDAV server a vaše kontakty/události budou synchronizovány oběma směry.

+ +

Pro více informací se podívejte na domovskou stránku aplikace DAVdroid. + Najdete tam i návod pro nastavení. DAVdroid respektuje + vaše soukromí, přečtěte si naši Privacy Policy.

+ +

If you use CyanogenMod, "Privacy Guard" must be disabled for DAVdroid. Otherwise, DAVdroid is not allowed to access + and synchronize your contacts and events.

+ +

In case of problems, please read the FAQ first. + If you encounter a bug that is clearly related to DAVdroid, enter it on + Github issues instead of contacting us directly or giving a poor + rating for the app.

+ +

Open-source

+

DAVdroid is designed to be an open-source project from the very first beginning. It is always possible to compile the + app yourself and use it for free without any obligations. The source code is + available on Github, and you can + download the app on F-droid.

+ +

However, it was much work to create this app, so we have decided to put it into the commercial stores for a small fee. + If you want to support this project, please consider donating to DAVdroid + or purchasing it.

+ +

License

+

Copyright (c) 2013 – 2014 Ricki Hirner, Bernhard Stockmann (bitfire web engineering). All rights reserved. + This program and the accompanying materials are made available under the terms of the GNU Public License v3.0 which + accompanies this distribution, and is available at http://www.gnu.org/licenses/gpl.html. As far as Google Play, Samsung + Store, AndroidPit App Center or Amazon Appstore require other terms, the respective terms apply for versions + that are downloaded via these services.

+ +

Překlad. + Čeština: Jaroslav Lichtblau +

+ +

Použité knihovny třetích stran

+

+ * Apache HttpClient (httpclientandroidlib flavour) – Apache License, Version 2.0
+ * iCal4jNew BSD License)
+ * ez-vcardNew BSD License
+ * Simple XML SerializationApache License, Version 2.0
+ * Project LombokMIT License
+ * dnsjavaBSD License

+ ]]>
+ + + Přihlášení s emailovou adresou + Detaily účtu budou automaticky detekovány podle doménového jména. Příkald: mujucet@icloud.com + Přihlášení s URL a uživatelským jménem + Detaily účtu budou automaticky rozpoznány podle URL a uživatelského jména. Nejčastější volba pro vlastní servery. + + Vložte prosím svou emailovou adresu. Její doménová adresa bude použita pro automatické zjištění nastavení služby. + Email: + + + http:// + https:// + + "Pokud nepoužijete šifrované připojení (HTTPS), ostatní budou moci lehce získat vaše přihlašovací údaje, kontakty a události." + Uživatelské jméno: + Kořenová URL (sbírky budou detekovány automaticky): + Preemptivní ověření (doporučeno, ale není kompatibilní s Digest ověřením) + + Heslo: + + DAVdroid: Vybrat sbírky + V tomto umístění není CalDAV-/CardDAV služba dostupná. + Přidat účet + Probíhá komunikace se serverem. Chvilku strpení… + Které sbírky mají být synchronizovány? + Adresáře + Adresář + Kalendáře + Kalendář + Vybrat alespoň jeden adresář (opakovaně tapnout pro odebrání): + Vybrat své kalendáře: + + Detaily účtu + Jméno účtu: + Můj CalDAV/CardDAV účet + Emailová adresa: + "ORGANIZÁTOR událostí; vyžadováno pokud budete přidávat účastníky" + "Použijte svou emailovou adresu jako jméno účtu. Android bude používat tuto hodnotu jako jméno ORGANIZÁTORA událostí které vytvoříte. Nelze mít dva účty se stejným jménem. + pouze pro čtení + + + Obecná nastavení + Ladící nastavení + Vypnout HTTP kompresi + HTTP komprese je vypnuta (ladící mód) + HTTP komprese je použita všude kde možno + Logovat síťový provoz + Veškerý síťový provoz je detailně logován (ladící mód) + Síťový provoz není logován + Nahlásit problém + +
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml new file mode 100644 index 00000000..d77ccd47 --- /dev/null +++ b/res/values-de/strings.xml @@ -0,0 +1,138 @@ + + + + + DAVdroid-Website + Weiter + Hilfe + + HTTP-Fehler: %s + Fehlende Server-Unterstützung: %s + E/A-Fehler: %s + URI ungültig: %s + + + Sync-Konten anzeigen + + Vielen Dank, dass Sie DAVdroid im Play Store erworben haben und das Projekt dadurch unterstützen. + Leider gibt es derzeit zwei Probleme, die vom Play Store verursacht werden:

+ +

1. DAVdroid-Accounts verschwinden nach einem Neustart

+

Möglicherweise verschwinden alle Ihre DAVdroid-Accounts samt Kontakten und Terminen nach einem Neustart + des Geräts. Die Ursache ist ein Fehler in Android, + der zur irrtümlichen Entfernung von Accounts von Bezahlapps bei einem Neustart führt, da die Prüfung auf verwaiste + und damit zu löschende Accounts schon *vor* dem Entschlüsseln der App erfolgt.

+

Betroffene Benutzer*Innen:
+ * alle mit Android 4.1, die DAVdroid über Play Store bezogen haben;
+ * einige mit Android 4.2, die DAVdroid über Play Store bezogen und bestimmte Geräte haben (zB die meisten Samsung-Geräte)

+ +

2. DAVdroid-Accounts verschwinden nach einer DAVdroid-Aktualisierung

+

Möglicherweise verschwinden alle Ihre DAVdroid-Accounts samt Kontakten und Terminen während eines + DAVdroid-Updates, das von Play Store durchgeführt wird. Die Ursache ist ein + anderer Fehler in Android, + der zur irrtümlichen Entfernung von Accounts von Bezahlapps bei der Aktualisierung dieser Apps führt.

+

Betroffene Benutzer*Innen:
+ einige mit Android 4.4.2, die DAVdroid über Play Store bezogen haben (bekannt sind Nexus-Geräte und Moto G)

+ +

Sollten Sie von einem dieser Fehler betroffen sein, installieren + Sie den DAVdroid JB Workaround.

+ ]]>
+ Willkommen bei DAVdroid/%s! + +

DAVdroid ist ein Android 4+-CalDAV/CardDAV-Sync-Adapter. Um ihn zu verwenden, müssen Sie ein DAVdroid-Konto + für Ihren Server hinzufügen. Die Kontakte/Termine werden dann automatisch in beide Richtungen synchronisiert.

+ +

Wenn Sie CyanogenMod benutzen, muss die "Datenschutz"-Funktion für DAVdroid deaktiviert sein, da DAVdroid sonst + keinen Zugriff auf die Kontakte und Kalendar hat und auch nicht synchronisieren kann.

+ +

Weitere Informationen erhalten Sie auf der DAVdroid-Homepage. + Dort finden Sie auch eine Anleitung zum Einrichten. + DAVdroid respektiert Ihre Privatsphäre (siehe Datenschutzrichtlinie).

+ +

Bei Problemen lesen Sie bitte die häufig gestellten Fragen. + Im Falle eines Fehlers, der eindeutig durch DAVdroid verursacht wird, berichten Sie diesen wenn möglich auf + Github Issues, anstatt uns direkt zu kontaktieren oder die App + schlecht zu bewerten.

+ +

Open-Source

+

DAVdroid ist von Anfang an als Open-Source-Projekt ausgelegt. Der Quellcode kann jederzeit selbst kompiliert und + die App unter den Bedingungen der GPLv3 verwendet werden. Der Quellcode ist + auf Github verfügbar, die App kann auch + über F-Droid bezogen werden.

+ +

Es ist jedoch viel Arbeit, die App zu entwickeln und besser zu machen. Daher haben wir uns entschlossen, sie + auch gegen eine kleine Gebühr in die kommerziellen App-Stores zu stellen. Wenn Sie das Projekt unterstützen wollen, können Sie + für DAVdroid spenden oder die App kaufen.

+ +

Lizenz

+

Copyright (c) 2013 – 2014 Ricki Hirner, Bernhard Stockmann (bitfire web engineering), alle Rechte + vorbehalten. Dieses Programm ist freie Software. Sie können es unter den Bedingungen der GNU + General Public License Version 3, wie von der Free Software Foundation veröffentlicht, weitergeben und/oder modifizieren. + Sofern Google Play oder Samsung Store andere Bedingungen benötigen, gelten für über den jeweiligen Markt heruntergeladene + Apps diese Bedingungen.

+ +

Die Übersetzung auf Deutsch wird von den Autoren zur Verfügung gestellt. Übersetzungen für andere Sprachen + wurden von verschiedenen Leuten beigetragen, die in der jeweiligen Sprachversion erwähnt sind.

+ +

Verwendete Bibliotheken

+

+ * Apache HttpClient (mittels httpclientandroidlib) – Apache License, Version 2.0
+ * iCal4jNew BSD License
+ * ez-vcardNew BSD License
+ * Simple XML SerializationApache License, Version 2.0
+ * Project LombokMIT License
+ * dnsjavaBSD License

+ ]]>
+ + + Mit Email-Adresse anmelden + Domänenname wird verwendet, um die Servereinstellungen herauszufinden. Beispiel: myaccount@icloud.com + Mit URL und Benutzername anmelden + Basis-URL und Benutzername werden verwendet, um die Servereinstellungen herauszufinden; z.B. bei einem eigenen Server. + + Geben Sie Ihre Email-Adresse ein. Der Domänenname wird verwendet, um die Servereinstellungen herauszufinden. + Email: + + Ohne Verschlüsselung (HTTPS) können Ihre Zugangsdaten, Kontakte und Termine leicht abgefangen werden. + Benutzername: + Basis-URL (Ordner werden automatisch gefunden): + Präemptive Authentifizierung (empfohlen, aber nicht kompatibel mit Digest-Auth.) + + Passwort: + + DAVdroid: Ordner auswählen + An dieser Adresse konnte kein CalDAV- oder CardDAV-Dienst gefunden werden. + Konto hinzufügen + Daten werden vom Server abgefragt. Bitte warten… + Welche Ordner sollen synchronisiert werden? + Adressbücher + Adressbuch + Kalender + Kalender + Ein oder kein Adressbuch auswählen (nochmal berühren, um abzuwählen): + Kalender zur Synchronisation auswählen: + + Konto-Details + Kontoname: + Mein CalDAV/CardDAV-Konto + Email-Adresse: + "ORGANIZER der von Ihnen angelegten Termine; notwendig für Teilnehmer-Info" + "Verwenden Sie Ihre Email-Adresse als Kontoname, da Android den Kontonamen als ORGANIZER-Feld in Terminen benutzt. Sie können keine zwei Konten mit dem gleichen Namen anlegen. + schreibgeschützt + + + Hilfe zu DAVdroid + + Allgemeine Einstellungen + Einstellungen zur Fehlersuche + HTTP-Komprimierung deaktivieren + HTTP-Komprimierung ist deaktiviert (zur Fehlersuche) + HTTP-Komprimierung wird verwendet, falls möglich + Netzwerkverkehr aufzeichnen + Der gesamte Netzwerkverkehr wird in den Android-Logs mitgeschrieben (zur Fehlersuche) + Netzwerkverkehr wird nicht aufgezeichnet + Problem berichten + +
\ No newline at end of file diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml new file mode 100644 index 00000000..da7c99c6 --- /dev/null +++ b/res/values-es/strings.xml @@ -0,0 +1,86 @@ + + + + DAVdroid + Ajustes + + + http:// + https:// + + + "Si no usas encriptación (HTTPS), otras personas pueden interceptar fácilmente tus datos de ingreso, contactos y eventos." + Nombre de usuario: + Contraseña: + URL raiz (colecciones autodetectadas): + Siguiente + Ayuda + URL base no válida: + DAVdroid: Selecciona las colecciones + I/O error: %s + URI no válida: %s + Se han perdido capacidades: %s + Ni CalDAV ni CardDAV están disponibles + Añadir cuenta + Consultando el servidor. Espera, por favor… + error HTTP: %s + Qué colecciones quieres sincronizar? + Agendas + Calendarios + Seleciona una agenda (pulsar de nuevo para desmarcar): + Selecciona tus calendarios: + Autentificación preferente (recomendado, pero incompatible con la autentificación de Digest) + + Ayuda de DAVdroid + Administrar las cuentas sincronizadas + Sitio web de DAVdroid + ¡Bienvenido a DAVdroid/%s! + +

DAVdroid es un adaptador de sincronización entre Android 4+ y CalDAV/CardDAV. Para usarlo, simplemente añade una cuenta DAVdroid para tu servidor de CalDAV/CardDAV, así tus contactos y eventos serán sincronizados en ambas direcciones.

+ +

Si usas CyanogenMod, debes desactivar "Privacy Guard" para DAVdroid. De otro modo, DAVdroid no tendrá autorización para acceder y sincronizar tus contactos y eventos.

+ +

Para más información, por favor lee la página de DAVdroid. + También hay una Guía de configuración. DAVdroid respeta tu privacidad, lee nuestra Politica de Privacidad.

+ +

Si tienes problemas, por favor lee las FAQ primero. + Si te encuentras un bug que esté claramente relacionado con DAVdroid, entra en + Github issues en lugar de contactar con nosotros directamente o de darle una mala votación a la aplicación.

+ +

Código abierto

+

DAVdroid ha sido diseñado como un proyecto de código abierto desde sus inicios. Siempre es posible compilar la app por ti mismo y usarla de forma gratuita sin ninguna obligación. El código fuente está disponible en Github, y puedes descargar la app desde F-droid.

+ +

Sin embargo, hay mucho trabajo detrás de la creación de esta aplicación, así que hemos decidido ponerla en las tiendas (Google Play, + Samsung Store) a cambio de un pequeño pago. + Si quieres apoyar este proyecto, por favor piensa en hacer una donación a DAVdroid + o en comprar la app.

+ +

Licencia

+

Copyright (c) 2013 – 2014 Richard Hirner (bitfire web engineering). Todos los derechos reservados. + Este programa y los materiales que la acompañan está disponible bajo las condiciones de la GNU Public License v3.0 que acompaña a esta distribución, y está a tu disposición en http://www.gnu.org/licenses/gpl.html. En lo relativo a Google Play or Samsung requieren otras condiciones, éstas han sido descargadas a través de estos servicios.

+ +

Translations. + Catalanian: @pokoli, + Chinese (simplified): @phy25, + Czech: Jaroslav Lichtblau, + Serbian: @pejakm, + Spanish: @xphnx +

+ +

Librerías third-party usadas

+

+ * iCal4j (Licencia New BSD)
+ * ez-vcard (Licencia New BSD)
+ * Simple XML Serialization (Licencia Apache Version 2.0)
+ * Project Lombok (Licencia MIT)

+ ]]>
+ Detalles de la cuenta + Nombre de la cuenta: + Mi cuenta CalDAV/CardDAV + Dirección de correo: + "ORGANIZADOR de tus eventos; se necesita si se usa información de los asistentes" + "Usa tu dirección de correo electrónico como nombre de cuenta porque Android usará el nombre de cuenta como campo de ORGANIZADOR para los eventos que crees. No puedes tener dos cuentas con el mismo nombre. + +
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml new file mode 100644 index 00000000..c6a8aa2f --- /dev/null +++ b/res/values-fr/strings.xml @@ -0,0 +1,93 @@ + + + + DAVdroid + Paramètres + + + http:// + https:// + + + Si vous n\'utilisez pas de chiffrement(HTTPS), d\'autres personnes peuvent facilement intercepter vos informations de connexion, contacts et événements. + Utilisateur: + Mot de passe: + URL racine(les collections seront autodétectées): + Suivant + Aide + Racine de l\'URL incorrecte: + DAVdroid: Sélectionnez les collections + Erreur I/O: %s + URI incorrecte: %s + Capacités manquantes: %s + Aucun CalDAV ou CardDAV disponible + Ajouter un compte + Interroge le serveur. Patientez svp. + Erreur HTTP: %s + Quelles collections doivent être synchronisées ? + Carnets d\'adresses + Agendas + Choisissez un carnet d\'adresses(toucher à nouveau pour désélectionner): + Choisissez vos agendas: + Authentification préventive(recommandé, mais incompatible avec l\'authentification Digest) + + Aide de DAVdroid + Gérer les comptes synchronisés + Site Web de DAVdroid + Bienvenue dans DAVdroid/%s! + +

DAVdroid est un connecteur de synhronisation entre Android 4+ et CalDAV/CardDAV. Pour l\'utiliser, il suffit d\'ajouter un compte DAVdroid + pour votre serveur CalDAV/CardDAV, et vos contacts/évènements seront synchronisés dans les deux sens.

+ +

Si vous utilisez CyanogenMod, "Privacy Guard" doit être désactivé pour DAVdroid. Sinon DAVdroid ne sera pas en mesure d\'accéder + et synchroniser vos contacts et évènements

+ +

Pour plus d\'informations, visitez la page d\'accueil de DAVdroid. + Il y a Un guide d\'installation également. DAVdroid respecte + votre vie privée, see our Politique de confidentialité.

+ +

En cas de problème, merci de lire la FAQ en premier lieu. + Si vous rencontrer un bug qui est clairement lié à DAVdroid, svp saisissez le sur + Github issues au lieu de nous contacter directement ou d\'attribuer une mauvaise appréciation à l\'application.

+ +

Open-source

+

DAVdroid est conçu depuis le début comme étant open-source. Il est toujours possible de le compiler vous-même l\'application + et l\'utiliser gratuitement sans autre obligation. Le code source est + disponible sur Github, et vous pouvez + télécharger l\'application sur F-droid.

+ +

Néanmoins étant donné que créer cette application nécessite du travail, nous avons décidé de la mettre sur les Stores (Google Play, + Samsung Store) pour un faible coût. + Si vous voulez aider ce projet faites un don à DAVdroid ou achetez le

+ +

License

+

Copyright (c) 2013 – 2014 Ricki Hirner (bitfire web engineering). All rights reserved. + Ce programme et les documents qui l\'accompagnent sont mis à disposition sous les termes de la Licence Public GNU v3.0 qui + accompagne cette distribution, et est disponible à http://www.gnu.org/licenses/gpl.html. En ce qui concerne Google Play ou Samsung Store, les conditions respectives s\'appliquent pour les versions qui sont téléchargées via ces services.

+ +

Translations. + Catalanian: @pokoli, + Chinese (simplified): @phy25, + Czech: Jaroslav Lichtblau, + Serbian: @pejakm, + Spanish: @xphnx +

+ +

Bibliothèques tiers

+

+ * iCal4j (New BSD License)
+ * ez-vcard (New BSD License)
+ * Simple XML Serialization (Apache License, Version 2.0)
+ * Project Lombok (MIT License)

+ ]]>
+ Détails du compte + Nom du compte: + Mon compte CalDAV/CardDAV + Adresse Email: + ORGANISATEUR de vos événements; nécessaire pour l\'information des participants + Utilisez votre adresse email en tant que nom de compte car Android utilise ce nom pour le champ ORGANISATEUR des évènements que vous créez." + Vous ne pouvez pas avoir deux comptes du même nom. + +
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml new file mode 100644 index 00000000..e8d159dd --- /dev/null +++ b/res/values-hu/strings.xml @@ -0,0 +1,147 @@ + + + + + DAVdroid + + DAVdroid web oldal + Tovább + Súgó + + HTTP hiba: %s + Hiányzó szolgáltatás: %s + I/O hiba: %s + Érvénytelen URI: %s + + + Szinkronizációs fiókok kezelése + + Köszönjük, hogy megvásárolta a DAVdroid alkalmazást a Google Play áruházban, támogatva ezzel a projektet. Sajnos, + a Google Play áruházzal két probléma is van:

+

1. A fiókok az eszköz újraindítása után eltűnhetnek

+

Előfordulhat, hogy az eszköz újraindítása után az összes DAVdroid fiók (beleértve hozzájuk tartozó + névjegyeket és eseményeket) eltűnik. Ezt egy + Android szoftverhiba okozza, melynek + következtében a nem ingyenes alkalmazásokhoz tartozó fiókok az eszköz indításakor törlődnek. A hiba hátterében az áll, + hogy a (kódolt) APK fájlok a létrehozó nélküli ("megárvult") fiókok törlése után töltődnek be.

+

Érintett felhasználók:
+ * minden Android 4.1 felhasználó, aki a DAVdroid alkalmazást a Google Play áruházból töltötte le;
+ * egyes Android 4.2 felhasználók, akik a DAVdroid alkalmazást a Google Play áruházból töltötték le, a használt eszköz + típusától függően (például egyes Samsung eszközök felhasználói)

+

2. A fiókok a DAVdroid frissítése után eltűnhetnek

+

Előfordulhat, hogy az összes DAVdroid fiók (beleértve a hozzájuk tartozó névjegyeket és eseményeket) + eltűnik, miután a Google Play áruház frissítette a DAVdroid alkalmazást. Ezt szintén egy, az előzőhöz hasonló, + Android szoftverhiba okozza. +

Érintett felhasználók:
+ * egyes Android 4.4.2 felhasználók, akik a DAVdroid alkalmazást a Google Play áruházból töltötték le, a használt eszköz + típusától függően (például Nexus és Moto G eszközök felhasználói)

+

A probléma a JB DAVdroid gyorsjavítás + telepítésével kiküszöbölhető.

+ ]]>
+ Üdvözöljük a DAVdroid/%s felhasználók között! +

DAVdroid egy Android 4+ CalDAV/CardDAV szinkronizációs adapter. Használatához hozzon létre egy DAVdroid fiókot a használni + kívánt CalDAV/CardDav szerverhez. Ezt követően a névjegyek és események szinkronizálva lesznek, mindkét irányban.

+

További információkat a DAVdroid + honlap tartalmaz. A beállításokkal kapcsolatban a + beállítások oldalon + találhat információkat. A DAVdroid tiszteletben tartja adatait bizalmasságát, részleteket az + adatkezelési nyilatkozat + tartalmaz.

+

Ha Ön CyanogenMod felhasználó, vonja ki a DAVdroidot a "Privacy Guard" hatálya alól, különben a DAVdroid nem fog tudni + hozzáférni a névjegyekhez és eseményekhez.

+

Probléma esetén olvassa el a + GYIK-et. Ha + egyértelműen a DAVdroidhoz köthető hibába ütközik, ahelyett, hogy közvetlenül keresne minket, vagy lepontozná az alkalmazást, + inkább a Problémák oldalon jelentse be.

+

Nyílt forráskód

+

A DAVdroid kezdettől fogva nyílt forráskódú projekt. Bármikor lehetősége van az alkalmazást lefordítani és használni, + mindenféle kötelezettség nélkül. A forráskód elérhető a Githubon, a + lefordított alkalmazás pedig az + F-droidon.

+

Mindemellett, az alkalmazás kifejlesztése sok munkát igényelt, ezért úgy döntöttünk, hogy az alkalmazást más áruházakban + is elérhetővé tesszük, egy szerény díj ellenében. Ha szeretné támogatni ezt a projektet, kérjük, fontolja meg az alkalmazás + megvásárlását vagy közvetlen + támogatását.

+

Licenc

+

Copyright (c) 2013 – 2014 Ricki Hirner (bitfire web engineering). Minden jog fenntartva. + Ez a program és a kapcsolódó anyagok a GNU Public License v3.0 hatálya alatt állnak, amely részét képezi a jelen csomagnak, és + amely elérhető + http://www.gnu.org/licenses/gpl.html oldalon. Amennyiben a Google Play, Samsung + Store, AndroidPit App Center vagy Amazon Appstore áruház eltérő feltételeket szab, akkor annak az áruháznak a szabályai + irányadóak, ahonnan az alkalmazást letöltötte.

+

Fordítások. + cseh: Jaroslav Lichtblau, + katalán: @pokoli, + kínai (egyszerűsített): @phy25, + magyar: Gábor J.Tóth, + szerb: @pejakm, + spanyol: @xphnx. +

+

Harmadik felek által fejlesztett programcsomagok

+

+ * Apache HttpClient ( + httpclientandroidlib változat) – Apache License, Version 2.0
+ * iCal4j + New BSD License
+ * ez-vcard + New BSD License
+ * Simple XML Serialization + Apache License, Version 2.0
+ * Project LombokMIT License

+ ]]>
+ + + Bejelentkezés email cím segítségével + A szolgáltatás részleteinek automatikus detektálása a tartománynév alapján történik. Példa: myaccount@icloud.com + Bejelentkezés URL és felhasználónév segítségével + A szolgáltatás részleteinek automatikus detektálása a kiinduló URL és a felhasználónév alapján történik. Elsősorban privát szolgáltatásoknál ajánlott. + + Kérjük, adja meg email címét. A szolgáltatás részleteinek automatikus detektálása ennek + tartománynév-része alapján fog történni. + Email: + + + http:// + https:// + + "Titkosítás (HTTPS) nélkül a bejelentkezési azonosítókat, névjegyeket és eseményeket könnyen megismerhetik mások is." + Felhasználónév: + Fiók URL (a gyűjtemények detektálása automatikus): + Preemptív authentikáció (ajánlott, de Digest authentikációval nem működik) + + Jelszó: + + DAVdroid: Gyűjtemény kiválasztása + Nincs CalDAV-/CardDAV szolgáltatás a megadott helyen. + Fiók hozzáadása + Kapcsolódás a szerverhez. Egy pillanat… + Melyik gyűjtemények legyenek szinkronizálva? + Címjegyzékek + Címjegyzék + Naptárak + Naptár + Egy címjegyzék választható (a kijelölés visszavonása újbóli érintéssel vagy másik tétel kiválasztásával): + Naptárak kiválasztása: + + További beállítások + A fiók neve: + CalDAV/CardDAV fiók + Email cím: + "Szervező (ORGANIZER mező értéke), résztvevők kezelése esetén" + "Használja az email címet fióknévként, mert később a létrehozandó események szervezőjeként (ORGANIZER mező) az Android ezt fogja használni. Két fiókot nem lehet azonos néven létrehozni. + csak olvasható + + + Általános beállítások + Hibakeresési beállítások + HTTP tömörítés kikapcsolása + HTTP tömörítés kikapcsolva (hibakeresés) + HTTP tömörítés bekapcsolva (ahol csak lehetséges) + Hálózati forgalom naplózása + A teljes hálózati forgalom részletes naplózása (hibakeresés) + A hálózati forgalom naplózása kikapcsolva + Probléma bejelentése + +
\ No newline at end of file diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml new file mode 100644 index 00000000..ebf462b7 --- /dev/null +++ b/res/values-sr/strings.xml @@ -0,0 +1,136 @@ + + + + + ДАВдроид + + ДАВдроид вебсајт + Следеће + Помоћ + + ХТТП грешка: %s + Недостају могућности: %s + У/И грешка: %s + Неисправан УРИ: %s + + + Управљај налозима синхронизације + + Хвала вам што сте купили ДАВдроид преко Google Play сервиса и тиме подржали овај пројекат. Нажалост, постоје два проблема са Google Play сервисом:

+ +

1. Налози могу нестати након поновног покретања

+

Може вам се десити да сви ваши ДАВдроид налози (укључујући контакте и догађаје) нестану након + поновног покретања вашег уређаја. Разлог је грешка у Андроиду + која узрокује уклањање налога плаћених апликација приликом покретања јер се (шифровани) АПК фајлови + учитавају након провере за налоге без апликација.

+

Захваћени корисници:
+ * сви корисници Андроида 4.1 који су инсталирали ДАВдроид са Play Store сервиса;
+ * корисници Андроида 4.2 који су инсталирали ДАВдроид са Play Store сервиса само на неким уређајима (нпр. већина Самсунгових уређаја)

+ +

2. Налози могу да нестану након надоградње ДАВдроида

+

Може вам се десити да сви ваши ДАВдроид налози (укључујући контакте и догађаје) нестану након + надоградње ДАВдроида. Разлог је опет грешка у Андроиду + која узрокује уклањање налога плаћених апликација приликом надоградње из сличног разлога.

+

Захваћени корисници: неки корисници Андроида 4.4.2 који су инсталирали ДАВдроид са Play Store сервиса (Нексус и Мото Г уређаји)

+ +

Ако имате један од наведених проблема, инсталирајте „DAVdroid JB Workaround“.

+ ]]>
+ Добро дошли у ДАВдроид/%s! + +

ДАВдроид (DAVdroid) је Андроид 4+ адаптер синхронизације за КалДАВ/КардДАВ (CalDAV/CardDAV). Да бисте га користили, + само додајте ДАВдроид налог за ваш КалДАВ/КардДАВ сервер и ваши контакти/догађаји ће бити синхронизовани у оба смера.

+ +

За више информација погледајте ДАВдроид вебсајт. + Постоји и водич за поставку. ДАВдроид поштује + вашу приватност, погледајте нашу политику приватности.

+ +

Ако користите Цијаноген Мод, „Privacy Guard“ мора бити онемогућен за ДАВдроид. У супротном, ДАВдроиду неће бити дозвољени + приступ и синхронизација ваших контаката и догађаја.

+ +

У случају проблема, најпре прочитајте најчешћа питања. + Ако вам се деси грешка која је сигурно везана за ДАВдроид, поднесите пријаву на + Гитхабовом пратиоцу уместо да нас контактирате директно или да апликацији дате лошу оцену.

+ +

Отворени код

+

ДАВдроид је испочетка дизајниран као пројекат отвореног кода. Увек је могуће да сами компајлирате + апликацију и да је слободно користите без икаквих обавеза. Изворни код је + доступан на Гитхабу, а можете и + преузети апликацију са Ф-Дроида.

+ +

Међутим, уложено је много рада у ову апликацију па смо одлучили да је ставимо у комерцијалне продавнице за малу накнаду. + Ако желите да подржите овај пројекат, донирајте ДАВдроиду + или га купите.

+ +

Лиценца

+

Ауторско право © 2013 – 2014 Рики Хирнер (Ricki Hirner), Бернхарт Стокман (Bernhard Stockmann) (Битфајер веб инжињеринг). Сва права задржана. + Овај програм и пратећи материјали су доступни под условима ГНУ-ове Јавне Лиценце в3.0 која је приложена, + и доступна на http://www.gnu.org/licenses/gpl.html. Ако сервиси „Google Play“, + „Samsung Store“, „AndroidPit App Center“ или „Amazon Appstore“ захтевају друге услове, исти важе за издања преузета са ових сервиса.

+ +

Превод на српски: @pejakm. Превод на немачки: аутори. Остале преводе + допринели су људи поменути у преводима одговарајућих језика.

+ +

Коришћене друге библиотеке

+

+ * Апачи ХТТП клијент (httpclientandroidlib издање) – Апачи лиценца, издање 2.0
+ * iCal4jНова БСД лиценца)
+ * ez-vcardНова БСД лиценца
+ * Симпле ИксМЛ серијализацијаАпачи лиценца, издање 2.0
+ * Пројекат ЛомбокМИТ лиценца

+ * dnsjavaБСД лиценца

+ ]]>
+ + + Пријавите се адресом е-поште + Детаљи сервиса ће бити аутоматски откривени по имену домена. Пример: mojnalog@icloud.com + Пријавите се УРЛ-ом и корисничким именом + Детаљи сервиса ће бити аутоматски откривени по почетном УРЛ-у и корисничком имену. Углавном се користи за самохостоване сервисе. + + Унесите вашу адресу е-поште. Име домена ће бити коришћено за аутоматско откривање поставки сервиса. + Е-адреса: + + + http:// + https:// + + "Ако не користите шифровање (ХТТПС), други људи вам лако могу пресрести детаље пријаве, контакте и догађаје." + Корисничко име: + Корени УРЛ (збирке ће бити аутоматски откривене): + Превентивна аутентификација (препоручено, али некомпатибилно са Дигест аутентификацијом) + + Лозинка: + + ДАВдроид: Изаберите збирке + Нема доступног КалДАВ/КардДАВ сервиса на овој локацији. + Додај налог + Шаљем упит серверу. Сачекајте… + Које збирке да синхронизујем? + Адресари + Адресар + Календари + Календар + Изаберите један адресар (додирните поново да поништите избор): + Изаберите ваше календаре: + + Детаљи налога + Име налога: + Мој КалДАВ/КардДАВ налог + Е-адреса: + "ОРГАНИЗАТОР ваших догађаја; потребно ако користите податке о учеснику" + "Користите вашу е-адресу као име налога јер Андроид користи име налога за поље ОРГАНИЗАТОР за догађаје које направите. Не можете имати два налога истог имена. + само-за-читање + + + Опште поставке + Поставке проналаска грешака + Онемогући ХТТП компресију + ХТТП компресија је онемогућена (режим проналаска грешака) + ХТТП компресија се користи кад год је могуће + Бележи мрежни саобраћај + Сав мрежни саобраћај се исцрпно бележи у дневник (режим проналаска грешака) + Мрежни саобраћај се не бележи у дневник + Пријави грешку + +
diff --git a/res/values-zh-rcn/strings.xml b/res/values-zh-rcn/strings.xml new file mode 100644 index 00000000..48694a4c --- /dev/null +++ b/res/values-zh-rcn/strings.xml @@ -0,0 +1,126 @@ + + + + + DAVdroid + + DAVdroid 网站 + 继续 + 帮助 + + HTTP 错误: %s + 服务器缺少功能: %s + I/O 错误: %s + URI 无效: %s + + + 管理同步账户 + + 感谢您在 Google Play 上购买 DAVdroid 支持本项目。然而 Google Play 版应用有两个问题:

+ +

1. 账户可能在重启后消失

+

您可能会遇到以下问题:您所有的 DAVdroid 同步账户(包括通讯录和日历)在设备重启后消失。这是由于 Android 系统的一个 BUG,开机时,在检查完孤立账户后才会载入(加密的)程序文件,从而导致了付费应用创建的账户被移除。

+

受影响的用户:
+ * 从 Play 商店安装 DAVdroid 的所有 Android 4.1 用户;
+ * 从 Play 商店安装 DAVdroid 的部分设备(如大部分的三星设备)的 Android 4.2 用户。

+ +

2. 账户可能会在升级 DAVdroid 后消失

+

您可能会遇到以下问题:您所有的 DAVdroid 同步账户(包括通讯录和日历)在使用 Play 商店升级 DAVdroid 后消失。这是 Android 系统的另一个 BUG,由于相似的原因,使得在升级应用后,付费应用创建的账户被移除。

+

受影响的用户:从 Play 商店安装 DAVdroid 的部分 Android 4.4.2 用户(如 Nexus 设备和 Moto G)。

+ +

如果您遇到了这些问题,请安装 + DAVdroid JB 修复程序

+ ]]>
+ 欢迎使用 DAVdroid/%s! + +

DAVdroid 是一个 Android 4+ 的 CalDAV/CardDAV 同步程序。开始使用本程序,只需增加一个 DAVdroid 账户,在其中设置 CalDAV/CardDAV 服务器,您的通讯录和日程就可以被双向同步了。

+ +

更多信息请查阅 DAVdroid 英文主页,其中包括首次使用的配置指南。DAVdroid 尊重您的隐私,请查阅我们的隐私政策

+ +

如果您使用 CyanogenMod 等修改版系统,请停用针对 DAVdroid 的“隐私防护”功能,否则 DAVdroid 无法访问、同步您的通讯录和日程。

+ +

如果您在使用中遇到问题,请先阅读 FAQ。如果您遇到了明显与 DAVdroid 有关的 BUG,请在 Github issues 上提交,不要直接联系我们,甚至给应用差评。

+ +

开源

+

DAVdroid 从一开始就是开源项目。您可以自己编译应用,并可以没有限制地免费使用。源代码 + 存放在 Github 上,您也可以 + 在 F-droid 上下载应用

+ +

然而,编写这个应用是一个大工程,所以我们已经决定把它作为付费应用放在应用商店上,收一笔小费。 + 如果您想支持这个项目,请考虑 给 DAVdroid 捐款 或购买其付费版。

+ +

许可

+

Copyright (c) 2013 – 2014 Ricki Hirner (bitfire web engineering). All rights reserved. + This program and the accompanying materials are made available under the terms of the GNU Public License v3.0 which + accompanies this distribution, and is available at http://www.gnu.org/licenses/gpl.html. As far as Google Play, Samsung + Store, AndroidPit App Center or Amazon Appstore require other terms, the respective terms apply for versions + that are downloaded via these services.

+ +

Translation for German is provided by the authors. Translation for Chinese Simplified is by @phy25. Translations to other languages have been contributed by + various people which are mentioned in their respective language version.

+ +

使用的第三方程序库

+

+ * Apache HttpClient (httpclientandroidlib flavour) – Apache License, Version 2.0
+ * iCal4jNew BSD License)
+ * ez-vcardNew BSD License
+ * Simple XML SerializationApache License, Version 2.0
+ * Project LombokMIT License
+ * dnsjavaBSD License

+ ]]>
+ + + 使用邮箱地址登录 + 服务器信息会通过域名进行发现。例如: myaccount@icloud.com + 使用 URL 和用户名登录 + 服务器信息会通过 URL 和用户名进行发现。个人搭建的服务通常使用此项。 + + 请输入您的邮箱地址。邮箱的域名会被用来自动发现服务器信息。 + Email: + + + http:// + https:// + + "如果不使用加密连接 (HTTPS),其他人将很容易获取到你的登录信息、通讯录和日程。" + 用户名: + 服务器根地址(集合会自动检测): + 抢先认证模式(推荐使用,但不兼容 Digest 认证方式) + + 密码: + + DAVdroid: 选择同步项 + 找不到可用的 CalDAV-/CardDAV 服务。 + 增加账户 + 正在请求,请稍等… + 需要同步哪些集合? + 通讯录 + 通讯录 + 日历 + 日历 + 最多选择一个通讯录:(再次点按可取消选择) + 选择日历: + + 账户信息 + 账户显示名: + 我的 CalDAV/CardDAV 账户 + Email 地址: + "日程的组织者 (ORGANIZER);如果您使用参与者信息,则必填" + "请使用您的 E-mail 地址作为账户名,因为 Android 会将帐户名用于您创建的日程的参与者 (ORGANIZER) 项。您不能有两个重名的账户。 + 只读 + + + 普通设置 + 调试设置 + 停用 HTTP 压缩 + HTTP 压缩已停用(调试模式) + HTTP 压缩会在可用时使用 + 记录网络传输 + 传输内容会被日志记录(调试模式) + 传输内容不会被日志记录 + 报告问题 + +
diff --git a/res/values/strings.xml b/res/values/strings.xml new file mode 100644 index 00000000..91a64e75 --- /dev/null +++ b/res/values/strings.xml @@ -0,0 +1,140 @@ + + + + + DAVdroid + + DAVdroid Web site + Next + Help + + HTTP error: %s + Missing capabilities: %s + I/O error: %s + Invalid URI: %s + + + Manage sync accounts + + Thank you for buying DAVdroid via Google Play and thus supporting the project. Unfortunately, there are two issues with Google Play:

+ +

1. Accounts may be gone after a reboot

+

You may encounter the problem that all your DAVdroid accounts (including contacts and events) are gone + after rebooting your device. The reason is a bug in Android + that causes accounts of paid apps to be removed on start-up because the (encrypted) APK files are + loaded after checking for orphaned accounts.

+

Affected users:
+ * all Android 4.1 users who have got DAVdroid from Play Store;
+ * Android 4.2 users who have got DAVdroid from Play Store only with certain devices (for instance, most Samsung devices)

+ +

2. Accounts may be gone after upgrading DAVdroid

+

You may encounter the problem that all your DAVdroid accounts (including contacts and events) when Play Store + updates DAVdroid. The reason is another bug in Android + that causes accounts of paid apps to be removed when upgrading for a similar reason.

+

Affected users: some Android 4.4.2 users who have got DAVdroid from Play Store (known for Nexus devices and Moto G)

+ +

If you\'re affected by one of these bugs, please install the + DAVdroid JB Workaround.

+ ]]>
+ Welcome to DAVdroid/%s! + +

DAVdroid is an Android 4+ sync adapter for CalDAV/CardDAV. To use it, just add a DAVdroid account + for your CalDAV/CardDAV server and your contacts/events will be synchronized in both directions.

+ +

For more information, please see the DAVdroid homepage. + There\'s a Setup guide, too. DAVdroid respects + your privacy, see our Privacy Policy.

+ +

If you use CyanogenMod, "Privacy Guard" must be disabled for DAVdroid. Otherwise, DAVdroid is not allowed to access + and synchronize your contacts and events.

+ +

In case of problems, please read the FAQ first. + If you encounter a bug that is clearly related to DAVdroid, enter it on + Github issues instead of contacting us directly or giving a poor + rating for the app.

+ +

Open-source

+

DAVdroid is designed to be an open-source project from the very first beginning. It is always possible to compile the + app yourself and use it for free without any obligations. The source code is + available on Github, and you can + download the app on F-droid.

+ +

However, it was much work to create this app, so we have decided to put it into the commercial stores for a small fee. + If you want to support this project, please consider donating to DAVdroid + or purchasing it.

+ +

License

+

Copyright (c) 2013 – 2014 Ricki Hirner, Bernhard Stockmann (bitfire web engineering). All rights reserved. + This program and the accompanying materials are made available under the terms of the GNU Public License v3.0 which + accompanies this distribution, and is available at http://www.gnu.org/licenses/gpl.html. As far as Google Play, Samsung + Store, AndroidPit App Center or Amazon Appstore require other terms, the respective terms apply for versions + that are downloaded via these services.

+ +

Translation for German is provided by the authors. Translations to other languages have been contributed by + various people which are mentioned in their respective language version.

+ +

Used third-party libraries

+

+ * Apache HttpClient (httpclientandroidlib flavour) – Apache License, Version 2.0
+ * iCal4jNew BSD License)
+ * ez-vcardNew BSD License
+ * Simple XML SerializationApache License, Version 2.0
+ * Project LombokMIT License
+ * dnsjavaBSD License

+ ]]>
+ + + Login with email address + Service details will be auto-detected by domain name. Example: myaccount@icloud.com + Login with URL and user name + Service details will be auto-detected by initial URL and user name. Mostly used for self-hosted services. + + Please enter your email address. Its domain name will be used to auto-detect service settings. + Email: + + + http:// + https:// + + "If you don't use encryption (HTTPS), other people may easily intercept your login details, contacts and events." + User name: + Base URL (collections will be auto-detected): + Preemptive authentication (recommended, but incompatible with Digest auth) + + Password: + + DAVdroid: Select collections + No CalDAV-/CardDAV service is available at this location. + Add account + Querying server. Please wait… + Which collections shall be synchronized? + Address books + Address book + Calendars + Calendar + Select up to one address book (tap again to unselect): + Select your calendars: + + Account details + Account name: + My CalDAV/CardDAV Account + Email address: + "ORGANIZER of your events; required if you use attendee info" + "Use your email address as account name because Android will use the account name as ORGANIZER field for events you create. You can't have two accounts with the same name. + read-only + + + General settings + Debug settings + Disable HTTP compression + HTTP compression is disabled (debug mode) + HTTP compression is used whenever possible + Log network traffic + All network traffic is being logged verbosely (debug mode) + Network traffic is not being logged + Report an issue + +
diff --git a/res/values/styles.xml b/res/values/styles.xml new file mode 100644 index 00000000..a9661327 --- /dev/null +++ b/res/values/styles.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/res/xml/account_authenticator.xml b/res/xml/account_authenticator.xml new file mode 100644 index 00000000..542950e6 --- /dev/null +++ b/res/xml/account_authenticator.xml @@ -0,0 +1,6 @@ + diff --git a/res/xml/account_prefs.xml b/res/xml/account_prefs.xml new file mode 100644 index 00000000..101de536 --- /dev/null +++ b/res/xml/account_prefs.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/res/xml/contacts.xml b/res/xml/contacts.xml new file mode 100644 index 00000000..10d6eb36 --- /dev/null +++ b/res/xml/contacts.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/xml/general_settings.xml b/res/xml/general_settings.xml new file mode 100644 index 00000000..be95c323 --- /dev/null +++ b/res/xml/general_settings.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/res/xml/sync_calendars.xml b/res/xml/sync_calendars.xml new file mode 100644 index 00000000..d2ac9b79 --- /dev/null +++ b/res/xml/sync_calendars.xml @@ -0,0 +1,7 @@ + diff --git a/res/xml/sync_contacts.xml b/res/xml/sync_contacts.xml new file mode 100644 index 00000000..3a35ac92 --- /dev/null +++ b/res/xml/sync_contacts.xml @@ -0,0 +1,7 @@ + diff --git a/src/at/bitfire/davdroid/ArrayUtils.java b/src/at/bitfire/davdroid/ArrayUtils.java new file mode 100644 index 00000000..f4f050db --- /dev/null +++ b/src/at/bitfire/davdroid/ArrayUtils.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid; + +import java.lang.reflect.Array; + +public class ArrayUtils { + + @SuppressWarnings("unchecked") + public static T[][] partition(T[] bigArray, int max) { + int nItems = bigArray.length; + int nPartArrays = (nItems + max-1)/max; + + T[][] partArrays = (T[][])Array.newInstance(bigArray.getClass().getComponentType(), nPartArrays, 0); + + // nItems is now the number of remaining items + for (int i = 0; nItems > 0; i++) { + int n = (nItems < max) ? nItems : max; + partArrays[i] = (T[])Array.newInstance(bigArray.getClass().getComponentType(), n); + System.arraycopy(bigArray, i*max, partArrays[i], 0, n); + + nItems -= n; + } + + return partArrays; + } + +} diff --git a/src/at/bitfire/davdroid/Constants.java b/src/at/bitfire/davdroid/Constants.java new file mode 100644 index 00000000..cd0a4907 --- /dev/null +++ b/src/at/bitfire/davdroid/Constants.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid; + +public class Constants { + public static final String + APP_VERSION = "0.6.8", + ACCOUNT_TYPE = "bitfire.at.davdroid", + WEB_URL_HELP = "https://davdroid.bitfire.at/configuration?pk_campaign=davdroid-app", + + SETTING_DISABLE_COMPRESSION = "disable_compression", + SETTING_NETWORK_LOGGING = "network_logging"; +} diff --git a/src/at/bitfire/davdroid/MainActivity.java b/src/at/bitfire/davdroid/MainActivity.java new file mode 100644 index 00000000..a665f6a0 --- /dev/null +++ b/src/at/bitfire/davdroid/MainActivity.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.provider.Settings; +import android.text.Html; +import android.text.method.LinkMovementMethod; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; +import at.bitfire.davdroid.syncadapter.AddAccountActivity; +import at.bitfire.davdroid.syncadapter.GeneralSettingsActivity; + +public class MainActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_main); + + TextView tvWorkaround = (TextView)findViewById(R.id.text_workaround); + if (fromPlayStore()) { + tvWorkaround.setVisibility(View.VISIBLE); + tvWorkaround.setText(Html.fromHtml(getString(R.string.html_main_workaround))); + tvWorkaround.setMovementMethod(LinkMovementMethod.getInstance()); + } + + TextView tvInfo = (TextView)findViewById(R.id.text_info); + tvInfo.setText(Html.fromHtml(getString(R.string.html_main_info, Constants.APP_VERSION))); + tvInfo.setMovementMethod(LinkMovementMethod.getInstance()); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.main_activity, menu); + return true; + } + + + public void addAccount(MenuItem item) { + Intent intent = new Intent(this, AddAccountActivity.class); + startActivity(intent); + } + + public void showDebugSettings(MenuItem item) { + Intent intent = new Intent(this, GeneralSettingsActivity.class); + startActivity(intent); + } + + public void showSyncSettings(MenuItem item) { + Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS); + startActivity(intent); + } + + public void showWebsite(MenuItem item) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(Constants.WEB_URL_HELP + "&pk_kwd=main-activity")); + startActivity(intent); + } + + + private boolean fromPlayStore() { + try { + return "com.android.vending".equals(getPackageManager().getInstallerPackageName("at.bitfire.davdroid")); + } catch(IllegalArgumentException e) { + } + return false; + } +} diff --git a/src/at/bitfire/davdroid/URLUtils.java b/src/at/bitfire/davdroid/URLUtils.java new file mode 100644 index 00000000..8c6860d0 --- /dev/null +++ b/src/at/bitfire/davdroid/URLUtils.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.annotation.SuppressLint; +import android.util.Log; + +@SuppressLint("DefaultLocale") +public class URLUtils { + private static final String TAG = "davdroid.URIUtils"; + + + public static String ensureTrailingSlash(String href) { + if (!href.endsWith("/")) { + Log.d(TAG, "Implicitly appending trailing slash to collection " + href); + return href + "/"; + } else + return href; + } + + public static URL ensureTrailingSlash(URL href) { + if (!href.getPath().endsWith("/")) + try { + URL newURL = new URL(href, href.getPath() + "/"); + + // "@" is the only character that is not encoded + //newURL = new URI(newURI.toString().replaceAll("@", "%40")); + + Log.d(TAG, "Implicitly appending trailing slash to collection " + href + " -> " + newURL); + return newURL; + } catch (MalformedURLException e) { + Log.e(TAG, "Couldn't append trailing slash to collection URI", e); + } + return href; + } + + + /** handles invalid URLs/paths as good as possible **/ + public static String sanitize(String original) { + if (original == null) + return null; + + Pattern p = Pattern.compile("^((https?:)?//([^/]+))?(.*)", Pattern.CASE_INSENSITIVE); + // $1: "http://hostname" or "https://hostname" or "//hostname" or empty (hostname may end with":port") + // $2: "http:" or "https:" or empty + // $3: hostname (may end with ":port") or empty + // $4: path or empty + + Matcher m = p.matcher(original); + if (m.matches()) { + String schema = m.group(2), + host = m.group(3), + path = m.group(4); + + if (host != null) + // sanitize host name (don't replace "[", "]", ":" for IP address literals and port numbers) + // "@" should be used for user name/password, but this case shouldn't appear in our URLs + for (char c : new char[] { '@', ' ', '<', '>', '"', '#', '{', '}', '|', '\\', '^', '~', '`' }) + host = host.replace(String.valueOf(c), "%" + Integer.toHexString(c)); + + if (path != null) + // rewrite reserved characters: + // ":" and "@" may be used in the host part but not in the path + // rewrite unsafe characters: + // " ", "<", ">", """, "#", "{", "}", "|", "\", "^", "~", "["], "]", "`" + // do not rewrite "%" because we assume that URLs should be already encoded correctly + for (char c : new char[] { ':', '@', ' ', '<', '>', '"', '#', '{', '}', '|', '\\', '^', '~', '[', ']', '`' }) + path = path.replace(String.valueOf(c), "%" + Integer.toHexString(c)); + + String url = (schema != null) ? schema : ""; + if (host != null) + url = url + "//" + host; + if (path != null) + url = url + path; + + if (!url.equals(original)) + Log.w(TAG, "Trying to repair invalid URL: " + original + " -> " + url); + return url; + + } else { + + Log.w(TAG, "Couldn't sanitize URL: " + original); + return original; + + } + } + +} diff --git a/src/at/bitfire/davdroid/resource/CalDavCalendar.java b/src/at/bitfire/davdroid/resource/CalDavCalendar.java new file mode 100644 index 00000000..770f479e --- /dev/null +++ b/src/at/bitfire/davdroid/resource/CalDavCalendar.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource; + +import java.net.MalformedURLException; + +import at.bitfire.davdroid.webdav.DavMultiget; +import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient; + +public class CalDavCalendar extends RemoteCollection { + //private final static String TAG = "davdroid.CalDavCalendar"; + + @Override + protected String memberContentType() { + return "text/calendar"; + } + + @Override + protected DavMultiget.Type multiGetType() { + return DavMultiget.Type.CALENDAR; + } + + @Override + protected Event newResourceSkeleton(String name, String ETag) { + return new Event(name, ETag); + } + + + public CalDavCalendar(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws MalformedURLException { + super(httpClient, baseURL, user, password, preemptiveAuth); + } +} diff --git a/src/at/bitfire/davdroid/resource/CardDavAddressBook.java b/src/at/bitfire/davdroid/resource/CardDavAddressBook.java new file mode 100644 index 00000000..eda04fb1 --- /dev/null +++ b/src/at/bitfire/davdroid/resource/CardDavAddressBook.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource; + +import java.net.MalformedURLException; + +import at.bitfire.davdroid.webdav.DavMultiget; +import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient; + +public class CardDavAddressBook extends RemoteCollection { + //private final static String TAG = "davdroid.CardDavAddressBook"; + + @Override + protected String memberContentType() { + return Contact.MIME_TYPE; + } + + @Override + protected DavMultiget.Type multiGetType() { + return DavMultiget.Type.ADDRESS_BOOK; + } + + @Override + protected Contact newResourceSkeleton(String name, String ETag) { + return new Contact(name, ETag); + } + + + public CardDavAddressBook(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws MalformedURLException { + super(httpClient, baseURL, user, password, preemptiveAuth); + } +} diff --git a/src/at/bitfire/davdroid/resource/Contact.java b/src/at/bitfire/davdroid/resource/Contact.java new file mode 100644 index 00000000..d301450c --- /dev/null +++ b/src/at/bitfire/davdroid/resource/Contact.java @@ -0,0 +1,410 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import org.apache.commons.lang.StringUtils; + +import android.util.Log; +import at.bitfire.davdroid.Constants; +import ezvcard.Ezvcard; +import ezvcard.VCard; +import ezvcard.VCardVersion; +import ezvcard.ValidationWarnings; +import ezvcard.parameter.EmailType; +import ezvcard.parameter.ImageType; +import ezvcard.parameter.TelephoneType; +import ezvcard.property.Address; +import ezvcard.property.Anniversary; +import ezvcard.property.Birthday; +import ezvcard.property.Categories; +import ezvcard.property.Email; +import ezvcard.property.FormattedName; +import ezvcard.property.Impp; +import ezvcard.property.Logo; +import ezvcard.property.Nickname; +import ezvcard.property.Note; +import ezvcard.property.Organization; +import ezvcard.property.Photo; +import ezvcard.property.RawProperty; +import ezvcard.property.Revision; +import ezvcard.property.Role; +import ezvcard.property.Sound; +import ezvcard.property.Source; +import ezvcard.property.StructuredName; +import ezvcard.property.Telephone; +import ezvcard.property.Title; +import ezvcard.property.Uid; +import ezvcard.property.Url; + + +/** + * Represents a contact. Locally, this is a Contact in the Android + * device; remote, this is a VCard. + */ +@ToString(callSuper = true) +public class Contact extends Resource { + private final static String TAG = "davdroid.Contact"; + + public final static String MIME_TYPE = "text/vcard"; + + public final static String + PROPERTY_STARRED = "X-DAVDROID-STARRED", + PROPERTY_PHONETIC_FIRST_NAME = "X-PHONETIC-FIRST-NAME", + PROPERTY_PHONETIC_MIDDLE_NAME = "X-PHONETIC-MIDDLE-NAME", + PROPERTY_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME", + PROPERTY_SIP = "X-SIP"; + + public final static EmailType EMAIL_TYPE_MOBILE = EmailType.get("X-MOBILE"); + + public final static TelephoneType + PHONE_TYPE_CALLBACK = TelephoneType.get("X-CALLBACK"), + PHONE_TYPE_COMPANY_MAIN = TelephoneType.get("X-COMPANY_MAIN"), + PHONE_TYPE_RADIO = TelephoneType.get("X-RADIO"), + PHONE_TYPE_ASSISTANT = TelephoneType.get("X-ASSISTANT"), + PHONE_TYPE_MMS = TelephoneType.get("X-MMS"); + + @Getter @Setter private String unknownProperties; + + @Getter @Setter private boolean starred; + + @Getter @Setter private String displayName, nickName; + @Getter @Setter private String prefix, givenName, middleName, familyName, suffix; + @Getter @Setter private String phoneticGivenName, phoneticMiddleName, phoneticFamilyName; + @Getter @Setter private String note; + @Getter @Setter private Organization organization; + @Getter @Setter private String jobTitle, jobDescription; + + @Getter @Setter private byte[] photo; + + @Getter @Setter private Anniversary anniversary; + @Getter @Setter private Birthday birthDay; + + @Getter private List phoneNumbers = new LinkedList(); + @Getter private List emails = new LinkedList(); + @Getter private List impps = new LinkedList(); + @Getter private List
addresses = new LinkedList
(); + @Getter private List categories = new LinkedList(); + @Getter private List URLs = new LinkedList(); + + + /* instance methods */ + + public Contact(String name, String ETag) { + super(name, ETag); + } + + public Contact(long localID, String resourceName, String eTag) { + super(localID, resourceName, eTag); + } + + + @Override + public void initialize() { + generateUID(); + name = uid + ".vcf"; + } + + protected void generateUID() { + uid = UUID.randomUUID().toString(); + } + + + /* VCard methods */ + + @Override + public void parseEntity(InputStream is) throws IOException { + VCard vcard = Ezvcard.parse(is).first(); + if (vcard == null) + return; + + // now work through all supported properties + // supported properties are removed from the VCard after parsing + // so that only unknown properties are left and can be stored separately + + // UID + Uid uid = vcard.getUid(); + if (uid != null) { + this.uid = uid.getValue(); + vcard.removeProperties(Uid.class); + } else { + Log.w(TAG, "Received VCard without UID, generating new one"); + generateUID(); + } + + // X-DAVDROID-STARRED + RawProperty starred = vcard.getExtendedProperty(PROPERTY_STARRED); + if (starred != null && starred.getValue() != null) { + this.starred = starred.getValue().equals("1"); + vcard.removeExtendedProperty(PROPERTY_STARRED); + } else + this.starred = false; + + // FN + FormattedName fn = vcard.getFormattedName(); + if (fn != null) { + displayName = fn.getValue(); + vcard.removeProperties(FormattedName.class); + } else + Log.w(TAG, "Received invalid VCard without FN (formatted name) property"); + + // N + StructuredName n = vcard.getStructuredName(); + if (n != null) { + prefix = StringUtils.join(n.getPrefixes(), " "); + givenName = n.getGiven(); + middleName = StringUtils.join(n.getAdditional(), " "); + familyName = n.getFamily(); + suffix = StringUtils.join(n.getSuffixes(), " "); + vcard.removeProperties(StructuredName.class); + } + + // phonetic names + RawProperty + phoneticFirstName = vcard.getExtendedProperty(PROPERTY_PHONETIC_FIRST_NAME), + phoneticMiddleName = vcard.getExtendedProperty(PROPERTY_PHONETIC_MIDDLE_NAME), + phoneticLastName = vcard.getExtendedProperty(PROPERTY_PHONETIC_LAST_NAME); + if (phoneticFirstName != null) { + phoneticGivenName = phoneticFirstName.getValue(); + vcard.removeExtendedProperty(PROPERTY_PHONETIC_FIRST_NAME); + } + if (phoneticMiddleName != null) { + this.phoneticMiddleName = phoneticMiddleName.getValue(); + vcard.removeExtendedProperty(PROPERTY_PHONETIC_MIDDLE_NAME); + } + if (phoneticLastName != null) { + phoneticFamilyName = phoneticLastName.getValue(); + vcard.removeExtendedProperty(PROPERTY_PHONETIC_LAST_NAME); + } + + // TEL + phoneNumbers = vcard.getTelephoneNumbers(); + vcard.removeProperties(Telephone.class); + + // EMAIL + emails = vcard.getEmails(); + vcard.removeProperties(Email.class); + + // PHOTO + for (Photo photo : vcard.getPhotos()) { + this.photo = photo.getData(); + vcard.removeProperties(Photo.class); + break; + } + + // ORG + organization = vcard.getOrganization(); + vcard.removeProperties(Organization.class); + // TITLE + for (Title title : vcard.getTitles()) { + jobTitle = title.getValue(); + vcard.removeProperties(Title.class); + break; + } + // ROLE + for (Role role : vcard.getRoles()) { + this.jobDescription = role.getValue(); + vcard.removeProperties(Role.class); + break; + } + + // IMPP + impps = vcard.getImpps(); + vcard.removeProperties(Impp.class); + + // NICKNAME + Nickname nicknames = vcard.getNickname(); + if (nicknames != null) { + if (nicknames.getValues() != null) + nickName = StringUtils.join(nicknames.getValues(), ", "); + vcard.removeProperties(Nickname.class); + } + + // NOTE + List notes = new LinkedList(); + for (Note note : vcard.getNotes()) + notes.add(note.getValue()); + if (!notes.isEmpty()) + note = StringUtils.join(notes, "\n---\n"); + vcard.removeProperties(Note.class); + + // ADR + addresses = vcard.getAddresses(); + vcard.removeProperties(Address.class); + + // CATEGORY + Categories categories = vcard.getCategories(); + if (categories != null) + this.categories = categories.getValues(); + vcard.removeProperties(Categories.class); + + // URL + for (Url url : vcard.getUrls()) + URLs.add(url.getValue()); + vcard.removeProperties(Url.class); + + // BDAY + birthDay = vcard.getBirthday(); + vcard.removeProperties(Birthday.class); + // ANNIVERSARY + anniversary = vcard.getAnniversary(); + vcard.removeProperties(Anniversary.class); + + // X-SIP + for (RawProperty sip : vcard.getExtendedProperties(PROPERTY_SIP)) + impps.add(new Impp("sip", sip.getValue())); + vcard.removeExtendedProperty(PROPERTY_SIP); + + // remove binary properties because of potential OutOfMemory / TransactionTooLarge exceptions + vcard.removeProperties(Logo.class); + vcard.removeProperties(Sound.class); + // remove properties that don't apply anymore + vcard.removeProperties(Revision.class); + vcard.removeProperties(Source.class); + // store all remaining properties into unknownProperties + if (!vcard.getProperties().isEmpty() || !vcard.getExtendedProperties().isEmpty()) + try { + unknownProperties = vcard.write(); + } catch(Exception e) { + Log.w(TAG, "Couldn't store unknown properties (maybe illegal syntax), dropping them"); + } + } + + + @Override + public ByteArrayOutputStream toEntity() throws IOException { + VCard vcard = null; + try { + if (unknownProperties != null) + vcard = Ezvcard.parse(unknownProperties).first(); + } catch (Exception e) { + Log.w(TAG, "Couldn't parse original property set, beginning from scratch"); + } + if (vcard == null) + vcard = new VCard(); + + if (uid != null) + vcard.setUid(new Uid(uid)); + else + Log.wtf(TAG, "Generating VCard without UID"); + + if (starred) + vcard.setExtendedProperty(PROPERTY_STARRED, "1"); + + if (displayName != null) + vcard.setFormattedName(displayName); + else if (organization != null && organization.getValues() != null && organization.getValues().get(0) != null) + vcard.setFormattedName(organization.getValues().get(0)); + else + Log.w(TAG, "No FN (formatted name) available to generate VCard"); + + // N + if (familyName != null || middleName != null || givenName != null) { + StructuredName n = new StructuredName(); + if (prefix != null) + for (String p : StringUtils.split(prefix)) + n.addPrefix(p); + n.setGiven(givenName); + if (middleName != null) + for (String middle : StringUtils.split(middleName)) + n.addAdditional(middle); + n.setFamily(familyName); + if (suffix != null) + for (String s : StringUtils.split(suffix)) + n.addSuffix(s); + vcard.setStructuredName(n); + } + + // phonetic names + if (phoneticGivenName != null) + vcard.addExtendedProperty(PROPERTY_PHONETIC_FIRST_NAME, phoneticGivenName); + if (phoneticMiddleName != null) + vcard.addExtendedProperty(PROPERTY_PHONETIC_MIDDLE_NAME, phoneticMiddleName); + if (phoneticFamilyName != null) + vcard.addExtendedProperty(PROPERTY_PHONETIC_LAST_NAME, phoneticFamilyName); + + // TEL + for (Telephone phoneNumber : phoneNumbers) + vcard.addTelephoneNumber(phoneNumber); + + // EMAIL + for (Email email : emails) + vcard.addEmail(email); + + // ORG, TITLE, ROLE + if (organization != null) + vcard.setOrganization(organization); + if (jobTitle != null) + vcard.addTitle(jobTitle); + if (jobDescription != null) + vcard.addRole(jobDescription); + + // IMPP + for (Impp impp : impps) + vcard.addImpp(impp); + + // NICKNAME + if (!StringUtils.isBlank(nickName)) + vcard.setNickname(nickName); + + // NOTE + if (!StringUtils.isBlank(note)) + vcard.addNote(note); + + // ADR + for (Address address : addresses) + vcard.addAddress(address); + + // CATEGORY + if (!categories.isEmpty()) + vcard.setCategories(categories.toArray(new String[0])); + + // URL + for (String url : URLs) + vcard.addUrl(url); + + // ANNIVERSARY + if (anniversary != null) + vcard.setAnniversary(anniversary); + // BDAY + if (birthDay != null) + vcard.setBirthday(birthDay); + + // PHOTO + if (photo != null) + vcard.addPhoto(new Photo(photo, ImageType.JPEG)); + + // PRODID, REV + vcard.setProductId("DAVdroid/" + Constants.APP_VERSION + " (ez-vcard/" + Ezvcard.VERSION + ")"); + vcard.setRevision(Revision.now()); + + // validate and print warnings + ValidationWarnings warnings = vcard.validate(VCardVersion.V3_0); + if (!warnings.isEmpty()) + Log.w(TAG, "Created potentially invalid VCard! " + warnings); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + Ezvcard + .write(vcard) + .version(VCardVersion.V3_0) + .versionStrict(false) + .prodId(false) // we provide our own PRODID + .go(os); + return os; + } +} diff --git a/src/at/bitfire/davdroid/resource/DavResourceFinder.java b/src/at/bitfire/davdroid/resource/DavResourceFinder.java new file mode 100644 index 00000000..888a1e2f --- /dev/null +++ b/src/at/bitfire/davdroid/resource/DavResourceFinder.java @@ -0,0 +1,299 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource; + +import java.io.Closeable; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.LinkedList; +import java.util.List; + +import org.xbill.DNS.Lookup; +import org.xbill.DNS.Record; +import org.xbill.DNS.SRVRecord; +import org.xbill.DNS.TXTRecord; +import org.xbill.DNS.TextParseException; +import org.xbill.DNS.Type; + +import android.content.Context; +import android.util.Log; +import at.bitfire.davdroid.R; +import at.bitfire.davdroid.webdav.DavException; +import at.bitfire.davdroid.webdav.DavHttpClient; +import at.bitfire.davdroid.webdav.DavIncapableException; +import at.bitfire.davdroid.webdav.HttpPropfind.Mode; +import at.bitfire.davdroid.webdav.NotAuthorizedException; +import at.bitfire.davdroid.webdav.WebDavResource; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient; +import ezvcard.VCardVersion; + +public class DavResourceFinder implements Closeable { + private final static String TAG = "davdroid.DavResourceFinder"; + + protected Context context; + protected CloseableHttpClient httpClient; + + + public DavResourceFinder(Context context) { + this.context = context; + + // disable compression and enable network logging for debugging purposes + httpClient = DavHttpClient.create(true, true); + } + + @Override + public void close() throws IOException { + httpClient.close(); + } + + + public void findResources(ServerInfo serverInfo) throws URISyntaxException, DavException, HttpException, IOException { + // CardDAV + WebDavResource principal = getCurrentUserPrincipal(serverInfo, "carddav"); + if (principal != null) { + serverInfo.setCardDAV(true); + + principal.propfind(Mode.HOME_SETS); + String pathAddressBooks = principal.getAddressbookHomeSet(); + if (pathAddressBooks != null) { + Log.i(TAG, "Found address book home set: " + pathAddressBooks); + + WebDavResource homeSetAddressBooks = new WebDavResource(principal, pathAddressBooks); + if (checkHomesetCapabilities(homeSetAddressBooks, "addressbook")) { + homeSetAddressBooks.propfind(Mode.CARDDAV_COLLECTIONS); + + List addressBooks = new LinkedList(); + if (homeSetAddressBooks.getMembers() != null) + for (WebDavResource resource : homeSetAddressBooks.getMembers()) + if (resource.isAddressBook()) { + Log.i(TAG, "Found address book: " + resource.getLocation().getPath()); + ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo( + ServerInfo.ResourceInfo.Type.ADDRESS_BOOK, + resource.isReadOnly(), + resource.getLocation().toString(), + resource.getDisplayName(), + resource.getDescription(), resource.getColor() + ); + + VCardVersion version = resource.getVCardVersion(); + if (version == null) + version = VCardVersion.V3_0; // VCard 3.0 MUST be supported + info.setVCardVersion(version); + + addressBooks.add(info); + } + serverInfo.setAddressBooks(addressBooks); + } else + Log.w(TAG, "Found address-book home set, but it doesn't advertise CardDAV support"); + } + } + + // CalDAV + principal = getCurrentUserPrincipal(serverInfo, "caldav"); + if (principal != null) { + serverInfo.setCalDAV(true); + + principal.propfind(Mode.HOME_SETS); + String pathCalendars = principal.getCalendarHomeSet(); + if (pathCalendars != null) { + Log.i(TAG, "Found calendar home set: " + pathCalendars); + + WebDavResource homeSetCalendars = new WebDavResource(principal, pathCalendars); + if (checkHomesetCapabilities(homeSetCalendars, "calendar-access")) { + homeSetCalendars.propfind(Mode.CALDAV_COLLECTIONS); + + List calendars = new LinkedList(); + if (homeSetCalendars.getMembers() != null) + for (WebDavResource resource : homeSetCalendars.getMembers()) + if (resource.isCalendar()) { + Log.i(TAG, "Found calendar: " + resource.getLocation().getPath()); + if (resource.getSupportedComponents() != null) { + // CALDAV:supported-calendar-component-set available + boolean supportsEvents = false; + for (String supportedComponent : resource.getSupportedComponents()) + if (supportedComponent.equalsIgnoreCase("VEVENT")) + supportsEvents = true; + if (!supportsEvents) { // ignore collections without VEVENT support + Log.i(TAG, "Ignoring this calendar because of missing VEVENT support"); + continue; + } + } + ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo( + ServerInfo.ResourceInfo.Type.CALENDAR, + resource.isReadOnly(), + resource.getLocation().toString(), + resource.getDisplayName(), + resource.getDescription(), resource.getColor() + ); + info.setTimezone(resource.getTimezone()); + calendars.add(info); + } + serverInfo.setCalendars(calendars); + } else + Log.w(TAG, "Found calendar home set, but it doesn't advertise CalDAV support"); + } + } + + if (!serverInfo.isCalDAV() && !serverInfo.isCardDAV()) + throw new DavIncapableException(context.getString(R.string.setup_neither_caldav_nor_carddav)); + + } + + + /** + * Finds the initial service URL from a given base URI (HTTP[S] or mailto URI, user name, password) + * @param serverInfo User-given service information (including base URI, i.e. HTTP[S] URL+user name+password or mailto URI and password) + * @param serviceName Service name ("carddav" or "caldav") + * @return Initial service URL (HTTP/HTTPS), without user credentials + * @throws URISyntaxException when the user-given URI is invalid + * @throws MalformedURLException when the user-given URI is invalid + */ + public URL getInitialContextURL(ServerInfo serverInfo, String serviceName) throws URISyntaxException, MalformedURLException { + String scheme = null, + domain = null; + int port = -1; + String path = "/"; + + URI baseURI = serverInfo.getBaseURI(); + if ("mailto".equalsIgnoreCase(baseURI.getScheme())) { + // mailto URIs + String mailbox = serverInfo.getBaseURI().getSchemeSpecificPart(); + + // determine service FQDN + int pos = mailbox.lastIndexOf("@"); + if (pos == -1) + throw new URISyntaxException(mailbox, "Missing @ sign"); + + scheme = "https"; + domain = mailbox.substring(pos + 1); + if (domain.isEmpty()) + throw new URISyntaxException(mailbox, "Missing domain name"); + } else { + // HTTP(S) URLs + scheme = baseURI.getScheme(); + domain = baseURI.getHost(); + port = baseURI.getPort(); + path = baseURI.getPath(); + } + + // try to determine FQDN and port number using SRV records + try { + String name = "_" + serviceName + "s._tcp." + domain; + Log.d(TAG, "Looking up SRV records for " + name); + Record[] records = new Lookup(name, Type.SRV).run(); + if (records != null && records.length >= 1) { + SRVRecord srv = selectSRVRecord(records); + + scheme = "https"; + domain = srv.getTarget().toString(true); + port = srv.getPort(); + Log.d(TAG, "Found " + serviceName + "s service for " + domain + " -> " + domain + ":" + port); + + // SRV record found, look for TXT record too (for initial context path) + records = new Lookup(name, Type.TXT).run(); + if (records != null && records.length >= 1) { + TXTRecord txt = (TXTRecord)records[0]; + for (Object o : txt.getStrings().toArray()) { + String segment = (String)o; + if (segment.startsWith("path=")) { + path = segment.substring(5); + Log.d(TAG, "Found initial context path for " + serviceName + " at " + domain + " -> " + path); + break; + } + } + } + } + } catch (TextParseException e) { + throw new URISyntaxException(domain, "Invalid domain name"); + } + + if (port != -1) + return new URL(scheme, domain, port, path); + else + return new URL(scheme, domain, path); + } + + + /** + * Detects the current-user-principal for a given WebDavResource. At first, /.well-known/ is tried. Only + * if no current-user-principal can be detected for the .well-known location, the given location of the resource + * is tried. + * @param resource Location that will be queried + * @param serviceName Well-known service name ("carddav", "caldav") + * @return WebDavResource of current-user-principal for the given service, or null if it can't be found + * + * TODO: If a TXT record is given, always use it instead of trying .well-known first + */ + WebDavResource getCurrentUserPrincipal(ServerInfo serverInfo, String serviceName) throws URISyntaxException, IOException, NotAuthorizedException { + URL initialURL = getInitialContextURL(serverInfo, serviceName); + if (initialURL != null) { + // determine base URL (host name and initial context path) + WebDavResource base = new WebDavResource(httpClient, + initialURL, + serverInfo.getUserName(), serverInfo.getPassword(), serverInfo.isAuthPreemptive()); + + // look for well-known service (RFC 5785) + try { + WebDavResource wellKnown = new WebDavResource(base, "/.well-known/" + serviceName); + wellKnown.propfind(Mode.CURRENT_USER_PRINCIPAL); + if (wellKnown.getCurrentUserPrincipal() != null) + return new WebDavResource(wellKnown, wellKnown.getCurrentUserPrincipal()); + } catch (NotAuthorizedException e) { + Log.w(TAG, "Not authorized for well-known " + serviceName + " service detection", e); + throw e; + } catch (URISyntaxException e) { + Log.w(TAG, "Well-known" + serviceName + " service detection failed because of invalid URIs", e); + } catch (HttpException e) { + Log.d(TAG, "Well-known " + serviceName + " service detection failed with HTTP error", e); + } catch (DavException e) { + Log.w(TAG, "Well-known " + serviceName + " service detection failed with unexpected DAV response", e); + } + + // fall back to user-given initial context path + try { + base.propfind(Mode.CURRENT_USER_PRINCIPAL); + if (base.getCurrentUserPrincipal() != null) + return new WebDavResource(base, base.getCurrentUserPrincipal()); + } catch (NotAuthorizedException e) { + Log.e(TAG, "Not authorized for querying principal", e); + throw e; + } catch (HttpException e) { + Log.e(TAG, "HTTP error when querying principal", e); + } catch (DavException e) { + Log.e(TAG, "DAV error when querying principal", e); + } + Log.i(TAG, "Couldn't find current-user-principal for service " + serviceName); + } + return null; + } + + public static boolean checkHomesetCapabilities(WebDavResource resource, String davCapability) throws URISyntaxException, IOException { + // check for necessary capabilities + try { + resource.options(); + if (resource.supportsDAV(davCapability) && + resource.supportsMethod("PROPFIND")) // check only for methods that MUST be available for home sets + return true; + } catch(HttpException e) { + // for instance, 405 Method not allowed + } + return false; + } + + + SRVRecord selectSRVRecord(Record[] records) { + if (records.length > 1) + Log.w(TAG, "Multiple SRV records not supported yet; using first one"); + return (SRVRecord)records[0]; + } + +} diff --git a/src/at/bitfire/davdroid/resource/Event.java b/src/at/bitfire/davdroid/resource/Event.java new file mode 100644 index 00000000..91f68012 --- /dev/null +++ b/src/at/bitfire/davdroid/resource/Event.java @@ -0,0 +1,368 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.util.Calendar; +import java.util.LinkedList; +import java.util.List; +import java.util.SimpleTimeZone; +import java.util.TimeZone; + +import lombok.Getter; +import lombok.NonNull; +import lombok.Setter; +import net.fortuna.ical4j.data.CalendarBuilder; +import net.fortuna.ical4j.data.CalendarOutputter; +import net.fortuna.ical4j.data.ParserException; +import net.fortuna.ical4j.model.Component; +import net.fortuna.ical4j.model.ComponentList; +import net.fortuna.ical4j.model.Date; +import net.fortuna.ical4j.model.DateTime; +import net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory; +import net.fortuna.ical4j.model.Property; +import net.fortuna.ical4j.model.PropertyList; +import net.fortuna.ical4j.model.TimeZoneRegistry; +import net.fortuna.ical4j.model.ValidationException; +import net.fortuna.ical4j.model.component.VAlarm; +import net.fortuna.ical4j.model.component.VEvent; +import net.fortuna.ical4j.model.component.VTimeZone; +import net.fortuna.ical4j.model.parameter.Value; +import net.fortuna.ical4j.model.property.Attendee; +import net.fortuna.ical4j.model.property.Clazz; +import net.fortuna.ical4j.model.property.DateProperty; +import net.fortuna.ical4j.model.property.Description; +import net.fortuna.ical4j.model.property.DtEnd; +import net.fortuna.ical4j.model.property.DtStart; +import net.fortuna.ical4j.model.property.Duration; +import net.fortuna.ical4j.model.property.ExDate; +import net.fortuna.ical4j.model.property.ExRule; +import net.fortuna.ical4j.model.property.LastModified; +import net.fortuna.ical4j.model.property.Location; +import net.fortuna.ical4j.model.property.Organizer; +import net.fortuna.ical4j.model.property.ProdId; +import net.fortuna.ical4j.model.property.RDate; +import net.fortuna.ical4j.model.property.RRule; +import net.fortuna.ical4j.model.property.Status; +import net.fortuna.ical4j.model.property.Summary; +import net.fortuna.ical4j.model.property.Transp; +import net.fortuna.ical4j.model.property.Uid; +import net.fortuna.ical4j.model.property.Version; +import net.fortuna.ical4j.util.SimpleHostInfo; +import net.fortuna.ical4j.util.UidGenerator; +import android.text.format.Time; +import android.util.Log; +import at.bitfire.davdroid.Constants; +import at.bitfire.davdroid.syncadapter.DavSyncAdapter; + + +public class Event extends Resource { + private final static String TAG = "davdroid.Event"; + + public final static String MIME_TYPE = "text/calendar"; + + private final static TimeZoneRegistry tzRegistry = new DefaultTimeZoneRegistryFactory().createRegistry(); + + @Getter @Setter private String summary, location, description; + + @Getter private DtStart dtStart; + @Getter private DtEnd dtEnd; + @Getter @Setter private Duration duration; + @Getter @Setter private RDate rdate; + @Getter @Setter private RRule rrule; + @Getter @Setter private ExDate exdate; + @Getter @Setter private ExRule exrule; + + @Getter @Setter private Boolean forPublic; + @Getter @Setter private Status status; + + @Getter @Setter private boolean opaque; + + @Getter @Setter private Organizer organizer; + @Getter private List attendees = new LinkedList(); + public void addAttendee(Attendee attendee) { + attendees.add(attendee); + } + + @Getter private List alarms = new LinkedList(); + public void addAlarm(VAlarm alarm) { + alarms.add(alarm); + } + + + public Event(String name, String ETag) { + super(name, ETag); + } + + public Event(long localID, String name, String ETag) { + super(localID, name, ETag); + } + + + @Override + public void initialize() { + generateUID(); + name = uid.replace("@", "_") + ".ics"; + } + + protected void generateUID() { + UidGenerator generator = new UidGenerator(new SimpleHostInfo(DavSyncAdapter.getAndroidID()), String.valueOf(android.os.Process.myPid())); + uid = generator.generateUid().getValue(); + } + + + @Override + @SuppressWarnings("unchecked") + public void parseEntity(@NonNull InputStream entity) throws IOException, InvalidResourceException { + net.fortuna.ical4j.model.Calendar ical; + try { + CalendarBuilder builder = new CalendarBuilder(); + ical = builder.build(entity); + + if (ical == null) + throw new InvalidResourceException("No iCalendar found"); + } catch (ParserException e) { + throw new InvalidResourceException(e); + } + + // event + ComponentList events = ical.getComponents(Component.VEVENT); + if (events == null || events.isEmpty()) + throw new InvalidResourceException("No VEVENT found"); + VEvent event = (VEvent)events.get(0); + + if (event.getUid() != null) + uid = event.getUid().getValue(); + else { + Log.w(TAG, "Received VEVENT without UID, generating new one"); + generateUID(); + } + + if ((dtStart = event.getStartDate()) == null || (dtEnd = event.getEndDate()) == null) + throw new InvalidResourceException("Invalid start time/end time/duration"); + + if (hasTime(dtStart)) { + validateTimeZone(dtStart); + validateTimeZone(dtEnd); + } + + // all-day events and "events on that day": + // * related UNIX times must be in UTC + // * must have a duration (set to one day if missing) + if (!hasTime(dtStart) && !dtEnd.getDate().after(dtStart.getDate())) { + Log.i(TAG, "Repairing iCal: DTEND := DTSTART+1"); + Calendar c = Calendar.getInstance(TimeZone.getTimeZone(Time.TIMEZONE_UTC)); + c.setTime(dtStart.getDate()); + c.add(Calendar.DATE, 1); + dtEnd.setDate(new Date(c.getTimeInMillis())); + } + + rrule = (RRule)event.getProperty(Property.RRULE); + rdate = (RDate)event.getProperty(Property.RDATE); + exrule = (ExRule)event.getProperty(Property.EXRULE); + exdate = (ExDate)event.getProperty(Property.EXDATE); + + if (event.getSummary() != null) + summary = event.getSummary().getValue(); + if (event.getLocation() != null) + location = event.getLocation().getValue(); + if (event.getDescription() != null) + description = event.getDescription().getValue(); + + status = event.getStatus(); + + opaque = true; + if (event.getTransparency() == Transp.TRANSPARENT) + opaque = false; + + organizer = event.getOrganizer(); + for (Object o : event.getProperties(Property.ATTENDEE)) + attendees.add((Attendee)o); + + Clazz classification = event.getClassification(); + if (classification != null) { + if (classification == Clazz.PUBLIC) + forPublic = true; + else if (classification == Clazz.CONFIDENTIAL || classification == Clazz.PRIVATE) + forPublic = false; + } + + this.alarms = event.getAlarms(); + } + + @Override + @SuppressWarnings("unchecked") + public ByteArrayOutputStream toEntity() throws IOException { + net.fortuna.ical4j.model.Calendar ical = new net.fortuna.ical4j.model.Calendar(); + ical.getProperties().add(Version.VERSION_2_0); + ical.getProperties().add(new ProdId("-//bitfire web engineering//DAVdroid " + Constants.APP_VERSION + " (ical4j 1.0.x)//EN")); + + VEvent event = new VEvent(); + PropertyList props = event.getProperties(); + + if (uid != null) + props.add(new Uid(uid)); + + props.add(dtStart); + if (dtEnd != null) + props.add(dtEnd); + if (duration != null) + props.add(duration); + + if (rrule != null) + props.add(rrule); + if (rdate != null) + props.add(rdate); + if (exrule != null) + props.add(exrule); + if (exdate != null) + props.add(exdate); + + if (summary != null && !summary.isEmpty()) + props.add(new Summary(summary)); + if (location != null && !location.isEmpty()) + props.add(new Location(location)); + if (description != null && !description.isEmpty()) + props.add(new Description(description)); + + if (status != null) + props.add(status); + if (!opaque) + props.add(Transp.TRANSPARENT); + + if (organizer != null) + props.add(organizer); + props.addAll(attendees); + + if (forPublic != null) + event.getProperties().add(forPublic ? Clazz.PUBLIC : Clazz.PRIVATE); + + event.getAlarms().addAll(alarms); + + props.add(new LastModified()); + ical.getComponents().add(event); + + // add VTIMEZONE components + net.fortuna.ical4j.model.TimeZone + tzStart = (dtStart == null ? null : dtStart.getTimeZone()), + tzEnd = (dtEnd == null ? null : dtEnd.getTimeZone()); + if (tzStart != null) + ical.getComponents().add(tzStart.getVTimeZone()); + if (tzEnd != null && tzEnd != tzStart) + ical.getComponents().add(tzEnd.getVTimeZone()); + + CalendarOutputter output = new CalendarOutputter(false); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + try { + output.output(ical, os); + } catch (ValidationException e) { + Log.e(TAG, "Generated invalid iCalendar"); + } + return os; + } + + + public long getDtStartInMillis() { + return dtStart.getDate().getTime(); + } + + public String getDtStartTzID() { + return getTzId(dtStart); + } + + public void setDtStart(long tsStart, String tzID) { + if (tzID == null) { // all-day + dtStart = new DtStart(new Date(tsStart)); + } else { + DateTime start = new DateTime(tsStart); + start.setTimeZone(tzRegistry.getTimeZone(tzID)); + dtStart = new DtStart(start); + } + } + + + public long getDtEndInMillis() { + return dtEnd.getDate().getTime(); + } + + public String getDtEndTzID() { + return getTzId(dtEnd); + } + + public void setDtEnd(long tsEnd, String tzID) { + if (tzID == null) { // all-day + dtEnd = new DtEnd(new Date(tsEnd)); + } else { + DateTime end = new DateTime(tsEnd); + end.setTimeZone(tzRegistry.getTimeZone(tzID)); + dtEnd = new DtEnd(end); + } + } + + + // helpers + + public boolean isAllDay() { + return !hasTime(dtStart); + } + + protected static boolean hasTime(DateProperty date) { + return date.getDate() instanceof DateTime; + } + + protected static String getTzId(DateProperty date) { + if (date.isUtc() || !hasTime(date)) + return Time.TIMEZONE_UTC; + else if (date.getTimeZone() != null) + return date.getTimeZone().getID(); + else if (date.getParameter(Value.TZID) != null) + return date.getParameter(Value.TZID).getValue(); + + // fallback + return Time.TIMEZONE_UTC; + } + + /* guess matching Android timezone ID */ + protected static void validateTimeZone(DateProperty date) { + if (date.isUtc() || !hasTime(date)) + return; + + String tzID = getTzId(date); + if (tzID == null) + return; + + String localTZ = Time.TIMEZONE_UTC; + + String availableTZs[] = SimpleTimeZone.getAvailableIDs(); + for (String availableTZ : availableTZs) + if (tzID.indexOf(availableTZ, 0) != -1) { + localTZ = availableTZ; + break; + } + + Log.d(TAG, "Assuming time zone " + localTZ + " for " + tzID); + date.setTimeZone(tzRegistry.getTimeZone(localTZ)); + } + + public static String TimezoneDefToTzId(String timezoneDef) throws IllegalArgumentException { + try { + if (timezoneDef != null) { + CalendarBuilder builder = new CalendarBuilder(); + net.fortuna.ical4j.model.Calendar cal = builder.build(new StringReader(timezoneDef)); + VTimeZone timezone = (VTimeZone)cal.getComponent(VTimeZone.VTIMEZONE); + return timezone.getTimeZoneId().getValue(); + } + } catch (Exception ex) { + Log.w(TAG, "Can't understand time zone definition, ignoring", ex); + } + throw new IllegalArgumentException(); + } +} diff --git a/src/at/bitfire/davdroid/resource/InvalidResourceException.java b/src/at/bitfire/davdroid/resource/InvalidResourceException.java new file mode 100644 index 00000000..ee24e1f6 --- /dev/null +++ b/src/at/bitfire/davdroid/resource/InvalidResourceException.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource; + +public class InvalidResourceException extends Exception { + private static final long serialVersionUID = 1593585432655578220L; + + public InvalidResourceException(String message) { + super(message); + } + + public InvalidResourceException(Throwable throwable) { + super(throwable); + } +} diff --git a/src/at/bitfire/davdroid/resource/LocalAddressBook.java b/src/at/bitfire/davdroid/resource/LocalAddressBook.java new file mode 100644 index 00000000..b8711cbb --- /dev/null +++ b/src/at/bitfire/davdroid/resource/LocalAddressBook.java @@ -0,0 +1,1007 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource; + +import java.io.IOException; +import java.io.InputStream; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import lombok.Cleanup; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.WordUtils; + +import android.accounts.Account; +import android.content.ContentProviderClient; +import android.content.ContentProviderOperation; +import android.content.ContentProviderOperation.Builder; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.res.AssetFileDescriptor; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.net.Uri; +import android.os.RemoteException; +import android.provider.ContactsContract.CommonDataKinds; +import android.provider.ContactsContract.CommonDataKinds.Email; +import android.provider.ContactsContract.CommonDataKinds.GroupMembership; +import android.provider.ContactsContract.CommonDataKinds.Im; +import android.provider.ContactsContract.CommonDataKinds.Nickname; +import android.provider.ContactsContract.CommonDataKinds.Note; +import android.provider.ContactsContract.CommonDataKinds.Organization; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.CommonDataKinds.Photo; +import android.provider.ContactsContract.CommonDataKinds.SipAddress; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; +import android.provider.ContactsContract.CommonDataKinds.Website; +import android.provider.ContactsContract.Data; +import android.provider.ContactsContract.Groups; +import android.provider.ContactsContract.RawContacts; +import android.util.Log; +import at.bitfire.davdroid.syncadapter.AccountSettings; +import ezvcard.parameter.AddressType; +import ezvcard.parameter.EmailType; +import ezvcard.parameter.ImppType; +import ezvcard.parameter.TelephoneType; +import ezvcard.property.Address; +import ezvcard.property.Anniversary; +import ezvcard.property.Birthday; +import ezvcard.property.DateOrTimeProperty; +import ezvcard.property.Impp; +import ezvcard.property.Telephone; + + +public class LocalAddressBook extends LocalCollection { + private final static String TAG = "davdroid.LocalAddressBook"; + + protected final static String COLUMN_UNKNOWN_PROPERTIES = RawContacts.SYNC3; + + + protected AccountSettings accountSettings; + + + /* database fields */ + + @Override + protected Uri entriesURI() { + return syncAdapterURI(RawContacts.CONTENT_URI); + } + + protected String entryColumnAccountType() { return RawContacts.ACCOUNT_TYPE; } + protected String entryColumnAccountName() { return RawContacts.ACCOUNT_NAME; } + + protected String entryColumnParentID() { return null; /* maybe use RawContacts.DATA_SET some day? */ } + protected String entryColumnID() { return RawContacts._ID; } + protected String entryColumnRemoteName() { return RawContacts.SOURCE_ID; } + protected String entryColumnETag() { return RawContacts.SYNC2; } + + protected String entryColumnDirty() { return RawContacts.DIRTY; } + protected String entryColumnDeleted() { return RawContacts.DELETED; } + + protected String entryColumnUID() { return RawContacts.SYNC1; } + + + + public LocalAddressBook(Account account, ContentProviderClient providerClient, AccountSettings accountSettings) { + super(account, providerClient); + this.accountSettings = accountSettings; + } + + + /* collection operations */ + + @Override + public long getId() { + return -1; + } + + @Override + public String getCTag() { + return accountSettings.getAddressBookCTag(); + } + + @Override + public void setCTag(String cTag) { + accountSettings.setAddressBookCTag(cTag); + } + + + /* create/update/delete */ + + public Contact newResource(long localID, String resourceName, String eTag) { + return new Contact(localID, resourceName, eTag); + } + + public void deleteAllExceptRemoteNames(Resource[] remoteResources) { + String where; + + if (remoteResources.length != 0) { + List sqlFileNames = new LinkedList(); + for (Resource res : remoteResources) + sqlFileNames.add(DatabaseUtils.sqlEscapeString(res.getName())); + where = entryColumnRemoteName() + " NOT IN (" + StringUtils.join(sqlFileNames, ",") + ")"; + } else + where = entryColumnRemoteName() + " IS NOT NULL"; + + Builder builder = ContentProviderOperation.newDelete(entriesURI()).withSelection(where, null); + pendingOperations.add(builder + .withYieldAllowed(true) + .build()); + } + + @Override + public void commit() throws LocalStorageException { + super.commit(); + + // update group details for groups we have just created + Uri groupsUri = syncAdapterURI(Groups.CONTENT_URI); + try { + // newly created groups don't have a TITLE + @Cleanup Cursor cursor = providerClient.query(groupsUri, + new String[] { Groups.SOURCE_ID }, + Groups.TITLE + " IS NULL", null, null + ); + while (cursor != null && cursor.moveToNext()) { + // found group, set TITLE to SOURCE_ID and other details + String sourceID = cursor.getString(0); + pendingOperations.add(ContentProviderOperation.newUpdate(groupsUri) + .withSelection(Groups.SOURCE_ID + "=?", new String[] { sourceID }) + .withValue(Groups.TITLE, sourceID) + .withValue(Groups.GROUP_VISIBLE, 1) + .build()); + super.commit(); + } + } catch (RemoteException e) { + throw new LocalStorageException("Couldn't update group names", e); + } + } + + + /* methods for populating the data object from the content provider */ + + @Override + public void populate(Resource res) throws LocalStorageException { + Contact c = (Contact)res; + + try { + @Cleanup Cursor cursor = providerClient.query(ContentUris.withAppendedId(entriesURI(), c.getLocalID()), + new String[] { entryColumnUID(), COLUMN_UNKNOWN_PROPERTIES, RawContacts.STARRED }, null, null, null); + if (cursor != null && cursor.moveToNext()) { + c.setUid(cursor.getString(0)); + c.setUnknownProperties(cursor.getString(1)); + c.setStarred(cursor.getInt(2) != 0); + } else + throw new RecordNotFoundException(); + + populateStructuredName(c); + populatePhoneNumbers(c); + populateEmailAddresses(c); + populatePhoto(c); + populateOrganization(c); + populateIMPPs(c); + populateNickname(c); + populateNote(c); + populatePostalAddresses(c); + populateCategories(c); + populateURLs(c); + populateEvents(c); + populateSipAddress(c); + } catch(RemoteException ex) { + throw new LocalStorageException(ex); + } + } + + private void populateStructuredName(Contact c) throws RemoteException { + @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { + /* 0 */ StructuredName.DISPLAY_NAME, StructuredName.PREFIX, StructuredName.GIVEN_NAME, + /* 3 */ StructuredName.MIDDLE_NAME, StructuredName.FAMILY_NAME, StructuredName.SUFFIX, + /* 6 */ StructuredName.PHONETIC_GIVEN_NAME, StructuredName.PHONETIC_MIDDLE_NAME, StructuredName.PHONETIC_FAMILY_NAME + }, StructuredName.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", + new String[] { String.valueOf(c.getLocalID()), StructuredName.CONTENT_ITEM_TYPE }, null); + + if (cursor != null && cursor.moveToNext()) { + c.setDisplayName(cursor.getString(0)); + + c.setPrefix(cursor.getString(1)); + c.setGivenName(cursor.getString(2)); + c.setMiddleName(cursor.getString(3)); + c.setFamilyName(cursor.getString(4)); + c.setSuffix(cursor.getString(5)); + + c.setPhoneticGivenName(cursor.getString(6)); + c.setPhoneticMiddleName(cursor.getString(7)); + c.setPhoneticFamilyName(cursor.getString(8)); + } + } + + protected void populatePhoneNumbers(Contact c) throws RemoteException { + @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Phone.TYPE, Phone.LABEL, Phone.NUMBER, Phone.IS_SUPER_PRIMARY }, + Phone.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", + new String[] { String.valueOf(c.getLocalID()), Phone.CONTENT_ITEM_TYPE }, null); + while (cursor != null && cursor.moveToNext()) { + ezvcard.property.Telephone number = new ezvcard.property.Telephone(cursor.getString(2)); + switch (cursor.getInt(0)) { + case Phone.TYPE_HOME: + number.addType(TelephoneType.HOME); + break; + case Phone.TYPE_MOBILE: + number.addType(TelephoneType.CELL); + break; + case Phone.TYPE_WORK: + number.addType(TelephoneType.WORK); + break; + case Phone.TYPE_FAX_WORK: + number.addType(TelephoneType.FAX); + number.addType(TelephoneType.WORK); + break; + case Phone.TYPE_FAX_HOME: + number.addType(TelephoneType.FAX); + number.addType(TelephoneType.HOME); + break; + case Phone.TYPE_PAGER: + number.addType(TelephoneType.PAGER); + break; + case Phone.TYPE_CALLBACK: + number.addType(Contact.PHONE_TYPE_CALLBACK); + break; + case Phone.TYPE_CAR: + number.addType(TelephoneType.CAR); + break; + case Phone.TYPE_COMPANY_MAIN: + number.addType(Contact.PHONE_TYPE_COMPANY_MAIN); + break; + case Phone.TYPE_ISDN: + number.addType(TelephoneType.ISDN); + break; + case Phone.TYPE_MAIN: + number.addType(TelephoneType.PREF); + break; + case Phone.TYPE_OTHER_FAX: + number.addType(TelephoneType.FAX); + break; + case Phone.TYPE_RADIO: + number.addType(Contact.PHONE_TYPE_RADIO); + break; + case Phone.TYPE_TELEX: + number.addType(TelephoneType.TEXTPHONE); + break; + case Phone.TYPE_TTY_TDD: + number.addType(TelephoneType.TEXT); + break; + case Phone.TYPE_WORK_MOBILE: + number.addType(TelephoneType.CELL); + number.addType(TelephoneType.WORK); + break; + case Phone.TYPE_WORK_PAGER: + number.addType(TelephoneType.PAGER); + number.addType(TelephoneType.WORK); + break; + case Phone.TYPE_ASSISTANT: + number.addType(Contact.PHONE_TYPE_ASSISTANT); + break; + case Phone.TYPE_MMS: + number.addType(Contact.PHONE_TYPE_MMS); + break; + case Phone.TYPE_CUSTOM: + String customType = cursor.getString(1); + if (!StringUtils.isEmpty(customType)) + number.addType(TelephoneType.get(labelToXName(customType))); + } + if (cursor.getInt(3) != 0) // IS_PRIMARY + number.addType(TelephoneType.PREF); + c.getPhoneNumbers().add(number); + } + } + + protected void populateEmailAddresses(Contact c) throws RemoteException { + @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Email.TYPE, Email.ADDRESS, Email.LABEL, Email.IS_SUPER_PRIMARY }, + Email.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", + new String[] { String.valueOf(c.getLocalID()), Email.CONTENT_ITEM_TYPE }, null); + while (cursor != null && cursor.moveToNext()) { + ezvcard.property.Email email = new ezvcard.property.Email(cursor.getString(1)); + switch (cursor.getInt(0)) { + case Email.TYPE_HOME: + email.addType(EmailType.HOME); + break; + case Email.TYPE_WORK: + email.addType(EmailType.WORK); + break; + case Email.TYPE_MOBILE: + email.addType(Contact.EMAIL_TYPE_MOBILE); + break; + case Email.TYPE_CUSTOM: + String customType = cursor.getString(2); + if (!StringUtils.isEmpty(customType)) + email.addType(EmailType.get(labelToXName(customType))); + } + if (cursor.getInt(3) != 0) // IS_PRIMARY + email.addType(EmailType.PREF); + c.getEmails().add(email); + } + } + + protected void populatePhoto(Contact c) throws RemoteException { + @Cleanup Cursor cursor = providerClient.query(dataURI(), + new String[] { Photo.PHOTO_FILE_ID, Photo.PHOTO }, + Photo.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", + new String[] { String.valueOf(c.getLocalID()), Photo.CONTENT_ITEM_TYPE }, null); + if (cursor != null && cursor.moveToNext()) { + if (!cursor.isNull(0)) { + Uri photoUri = Uri.withAppendedPath( + ContentUris.withAppendedId(RawContacts.CONTENT_URI, c.getLocalID()), + RawContacts.DisplayPhoto.CONTENT_DIRECTORY); + try { + @Cleanup AssetFileDescriptor fd = providerClient.openAssetFile(photoUri, "r"); + @Cleanup InputStream is = fd.createInputStream(); + c.setPhoto(IOUtils.toByteArray(is)); + } catch(IOException ex) { + Log.w(TAG, "Couldn't read high-res contact photo", ex); + } + } else + c.setPhoto(cursor.getBlob(1)); + } + } + + protected void populateOrganization(Contact c) throws RemoteException { + @Cleanup Cursor cursor = providerClient.query(dataURI(), + new String[] { Organization.COMPANY, Organization.DEPARTMENT, Organization.TITLE, Organization.JOB_DESCRIPTION }, + Organization.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", + new String[] { String.valueOf(c.getLocalID()), Organization.CONTENT_ITEM_TYPE }, null); + if (cursor != null && cursor.moveToNext()) { + String company = cursor.getString(0), + department = cursor.getString(1), + title = cursor.getString(2), + role = cursor.getString(3); + if (!StringUtils.isEmpty(company) || !StringUtils.isEmpty(department)) { + ezvcard.property.Organization org = new ezvcard.property.Organization(); + if (!StringUtils.isEmpty(company)) + org.addValue(company); + if (!StringUtils.isEmpty(department)) + org.addValue(department); + c.setOrganization(org); + } + if (!StringUtils.isEmpty(title)) + c.setJobTitle(title); + if (!StringUtils.isEmpty(role)) + c.setJobDescription(role); + } + } + + protected void populateIMPPs(Contact c) throws RemoteException { + @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Im.DATA, Im.TYPE, Im.LABEL, Im.PROTOCOL, Im.CUSTOM_PROTOCOL }, + Im.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", + new String[] { String.valueOf(c.getLocalID()), Im.CONTENT_ITEM_TYPE }, null); + while (cursor != null && cursor.moveToNext()) { + String handle = cursor.getString(0); + + Impp impp = null; + switch (cursor.getInt(3)) { + case Im.PROTOCOL_AIM: + impp = Impp.aim(handle); + break; + case Im.PROTOCOL_MSN: + impp = Impp.msn(handle); + break; + case Im.PROTOCOL_YAHOO: + impp = Impp.yahoo(handle); + break; + case Im.PROTOCOL_SKYPE: + impp = Impp.skype(handle); + break; + case Im.PROTOCOL_QQ: + impp = new Impp("qq", handle); + break; + case Im.PROTOCOL_GOOGLE_TALK: + impp = new Impp("google-talk", handle); + break; + case Im.PROTOCOL_ICQ: + impp = Impp.icq(handle); + break; + case Im.PROTOCOL_JABBER: + impp = Impp.xmpp(handle); + break; + case Im.PROTOCOL_NETMEETING: + impp = new Impp("netmeeting", handle); + break; + case Im.PROTOCOL_CUSTOM: + impp = new Impp(cursor.getString(4), handle); + } + + if (impp != null) { + switch (cursor.getInt(1)) { + case Im.TYPE_HOME: + impp.addType(ImppType.HOME); + break; + case Im.TYPE_WORK: + impp.addType(ImppType.WORK); + break; + case Im.TYPE_CUSTOM: + String customType = cursor.getString(2); + if (!StringUtils.isEmpty(customType)) + impp.addType(ImppType.get(labelToXName(customType))); + } + c.getImpps().add(impp); + } + } + } + + protected void populateNickname(Contact c) throws RemoteException { + @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Nickname.NAME }, + Nickname.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", + new String[] { String.valueOf(c.getLocalID()), Nickname.CONTENT_ITEM_TYPE }, null); + if (cursor != null && cursor.moveToNext()) + c.setNickName(cursor.getString(0)); + } + + protected void populateNote(Contact c) throws RemoteException { + @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Note.NOTE }, + Note.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", + new String[] { String.valueOf(c.getLocalID()), Note.CONTENT_ITEM_TYPE }, null); + if (cursor != null && cursor.moveToNext()) + c.setNote(cursor.getString(0)); + } + + protected void populatePostalAddresses(Contact c) throws RemoteException { + @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { + /* 0 */ StructuredPostal.FORMATTED_ADDRESS, StructuredPostal.TYPE, StructuredPostal.LABEL, + /* 3 */ StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD, + /* 6 */ StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE, + /* 9 */ StructuredPostal.COUNTRY + }, StructuredPostal.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", + new String[] { String.valueOf(c.getLocalID()), StructuredPostal.CONTENT_ITEM_TYPE }, null); + while (cursor != null && cursor.moveToNext()) { + Address address = new Address(); + + address.setLabel(cursor.getString(0)); + switch (cursor.getInt(1)) { + case StructuredPostal.TYPE_HOME: + address.addType(AddressType.HOME); + break; + case StructuredPostal.TYPE_WORK: + address.addType(AddressType.WORK); + break; + case StructuredPostal.TYPE_CUSTOM: + String customType = cursor.getString(2); + if (!StringUtils.isEmpty(customType)) + address.addType(AddressType.get(labelToXName(customType))); + break; + } + address.setStreetAddress(cursor.getString(3)); + address.setPoBox(cursor.getString(4)); + address.setExtendedAddress(cursor.getString(5)); + address.setLocality(cursor.getString(6)); + address.setRegion(cursor.getString(7)); + address.setPostalCode(cursor.getString(8)); + address.setCountry(cursor.getString(9)); + c.getAddresses().add(address); + } + } + + protected void populateCategories(Contact c) throws RemoteException { + @Cleanup Cursor cursorMemberships = providerClient.query(dataURI(), + new String[] { GroupMembership.GROUP_ROW_ID, GroupMembership.GROUP_SOURCE_ID }, + GroupMembership.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", + new String[] { String.valueOf(c.getLocalID()), GroupMembership.CONTENT_ITEM_TYPE }, null); + List categories = c.getCategories(); + while (cursorMemberships != null && cursorMemberships.moveToNext()) { + long rowID = cursorMemberships.getLong(0); + String sourceID = cursorMemberships.getString(1); + + // either a row ID or a source ID must be available + String where, whereArg; + if (sourceID == null) { + where = Groups._ID + "=?"; + whereArg = String.valueOf(rowID); + } else { + where = Groups.SOURCE_ID + "=?"; + whereArg = sourceID; + } + where += " AND " + Groups.DELETED + "=0"; // ignore deleted groups + Log.d(TAG, "Populating group from " + where + " " + whereArg); + + // fetch group + @Cleanup Cursor cursorGroups = providerClient.query(Groups.CONTENT_URI, + new String[] { Groups.TITLE }, + where, new String[] { whereArg }, null + ); + if (cursorGroups != null && cursorGroups.moveToNext()) { + String title = cursorGroups.getString(0); + + if (sourceID == null) { // Group wasn't created by DAVdroid + // SOURCE_ID IS NULL <=> _ID IS NOT NULL + Log.d(TAG, "Setting SOURCE_ID of non-DAVdroid group to title: " + title); + + ContentValues v = new ContentValues(1); + v.put(Groups.SOURCE_ID, title); + v.put(Groups.GROUP_IS_READ_ONLY, 0); + v.put(Groups.GROUP_VISIBLE, 1); + providerClient.update(syncAdapterURI(Groups.CONTENT_URI), v, Groups._ID + "=?", new String[] { String.valueOf(rowID) }); + + sourceID = title; + } + + // add group to CATEGORIES + if (sourceID != null) + categories.add(sourceID); + } else + Log.d(TAG, "Group not found (maybe deleted)"); + } + } + + protected void populateURLs(Contact c) throws RemoteException { + @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Website.URL }, + Website.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", + new String[] { String.valueOf(c.getLocalID()), Website.CONTENT_ITEM_TYPE }, null); + while (cursor != null && cursor.moveToNext()) + c.getURLs().add(cursor.getString(0)); + } + + protected void populateEvents(Contact c) throws RemoteException { + @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { CommonDataKinds.Event.TYPE, CommonDataKinds.Event.START_DATE }, + Photo.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", + new String[] { String.valueOf(c.getLocalID()), CommonDataKinds.Event.CONTENT_ITEM_TYPE }, null); + while (cursor != null && cursor.moveToNext()) { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US); + try { + Date date = formatter.parse(cursor.getString(1)); + switch (cursor.getInt(0)) { + case CommonDataKinds.Event.TYPE_ANNIVERSARY: + c.setAnniversary(new Anniversary(date)); + break; + case CommonDataKinds.Event.TYPE_BIRTHDAY: + c.setBirthDay(new Birthday(date)); + break; + } + } catch (ParseException e) { + Log.w(TAG, "Couldn't parse local birthday/anniversary date", e); + } + } + } + + protected void populateSipAddress(Contact c) throws RemoteException { + @Cleanup Cursor cursor = providerClient.query(dataURI(), + new String[] { SipAddress.SIP_ADDRESS, SipAddress.TYPE, SipAddress.LABEL }, + SipAddress.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", + new String[] { String.valueOf(c.getLocalID()), SipAddress.CONTENT_ITEM_TYPE }, null); + if (cursor != null && cursor.moveToNext()) { + Impp impp = new Impp("sip:" + cursor.getString(0)); + switch (cursor.getInt(1)) { + case SipAddress.TYPE_HOME: + impp.addType(ImppType.HOME); + break; + case SipAddress.TYPE_WORK: + impp.addType(ImppType.WORK); + break; + case SipAddress.TYPE_CUSTOM: + String customType = cursor.getString(2); + if (!StringUtils.isEmpty(customType)) + impp.addType(ImppType.get(labelToXName(customType))); + } + c.getImpps().add(impp); + } + } + + + /* content builder methods */ + + @Override + protected Builder buildEntry(Builder builder, Resource resource) { + Contact contact = (Contact)resource; + + return builder + .withValue(RawContacts.ACCOUNT_NAME, account.name) + .withValue(RawContacts.ACCOUNT_TYPE, account.type) + .withValue(entryColumnRemoteName(), contact.getName()) + .withValue(entryColumnUID(), contact.getUid()) + .withValue(entryColumnETag(), contact.getETag()) + .withValue(COLUMN_UNKNOWN_PROPERTIES, contact.getUnknownProperties()) + .withValue(RawContacts.STARRED, contact.isStarred() ? 1 : 0); + } + + + @Override + protected void addDataRows(Resource resource, long localID, int backrefIdx) { + Contact contact = (Contact)resource; + + queueOperation(buildStructuredName(newDataInsertBuilder(localID, backrefIdx), contact)); + + for (Telephone number : contact.getPhoneNumbers()) + queueOperation(buildPhoneNumber(newDataInsertBuilder(localID, backrefIdx), number)); + + for (ezvcard.property.Email email : contact.getEmails()) + queueOperation(buildEmail(newDataInsertBuilder(localID, backrefIdx), email)); + + if (contact.getPhoto() != null) + queueOperation(buildPhoto(newDataInsertBuilder(localID, backrefIdx), contact.getPhoto())); + + queueOperation(buildOrganization(newDataInsertBuilder(localID, backrefIdx), contact)); + + for (Impp impp : contact.getImpps()) + queueOperation(buildIMPP(newDataInsertBuilder(localID, backrefIdx), impp)); + + if (contact.getNickName() != null) + queueOperation(buildNickName(newDataInsertBuilder(localID, backrefIdx), contact.getNickName())); + + if (contact.getNote() != null) + queueOperation(buildNote(newDataInsertBuilder(localID, backrefIdx), contact.getNote())); + + for (Address address : contact.getAddresses()) + queueOperation(buildAddress(newDataInsertBuilder(localID, backrefIdx), address)); + + for (String category : contact.getCategories()) + queueOperation(buildGroupMembership(newDataInsertBuilder(localID, backrefIdx), category)); + + for (String url : contact.getURLs()) + queueOperation(buildURL(newDataInsertBuilder(localID, backrefIdx), url)); + + // events + if (contact.getAnniversary() != null) + queueOperation(buildEvent(newDataInsertBuilder(localID, backrefIdx), contact.getAnniversary(), CommonDataKinds.Event.TYPE_ANNIVERSARY)); + if (contact.getBirthDay() != null) + queueOperation(buildEvent(newDataInsertBuilder(localID, backrefIdx), contact.getBirthDay(), CommonDataKinds.Event.TYPE_BIRTHDAY)); + + // TODO relations + + // SIP addresses are built by buildIMPP + } + + @Override + protected void removeDataRows(Resource resource) { + pendingOperations.add(ContentProviderOperation.newDelete(dataURI()) + .withSelection(Data.RAW_CONTACT_ID + "=?", + new String[] { String.valueOf(resource.getLocalID()) }).build()); + } + + + protected Builder buildStructuredName(Builder builder, Contact contact) { + return builder + .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) + .withValue(StructuredName.PREFIX, contact.getPrefix()) + .withValue(StructuredName.DISPLAY_NAME, contact.getDisplayName()) + .withValue(StructuredName.GIVEN_NAME, contact.getGivenName()) + .withValue(StructuredName.MIDDLE_NAME, contact.getMiddleName()) + .withValue(StructuredName.FAMILY_NAME, contact.getFamilyName()) + .withValue(StructuredName.SUFFIX, contact.getSuffix()) + .withValue(StructuredName.PHONETIC_GIVEN_NAME, contact.getPhoneticGivenName()) + .withValue(StructuredName.PHONETIC_MIDDLE_NAME, contact.getPhoneticMiddleName()) + .withValue(StructuredName.PHONETIC_FAMILY_NAME, contact.getPhoneticFamilyName()); + } + + protected Builder buildPhoneNumber(Builder builder, Telephone number) { + int typeCode = Phone.TYPE_OTHER; + String typeLabel = null; + boolean is_primary = false; + + Set types = number.getTypes(); + // preferred number? + if (types.contains(TelephoneType.PREF)) + is_primary = true; + + // 1 Android type <-> 2 VCard types: fax, cell, pager + if (types.contains(TelephoneType.FAX)) { + if (types.contains(TelephoneType.HOME)) + typeCode = Phone.TYPE_FAX_HOME; + else if (types.contains(TelephoneType.WORK)) + typeCode = Phone.TYPE_FAX_WORK; + else + typeCode = Phone.TYPE_OTHER_FAX; + } else if (types.contains(TelephoneType.CELL)) { + if (types.contains(TelephoneType.WORK)) + typeCode = Phone.TYPE_WORK_MOBILE; + else + typeCode = Phone.TYPE_MOBILE; + } else if (types.contains(TelephoneType.PAGER)) { + if (types.contains(TelephoneType.WORK)) + typeCode = Phone.TYPE_WORK_PAGER; + else + typeCode = Phone.TYPE_PAGER; + // types with 1:1 translation + } else if (types.contains(TelephoneType.HOME)) { + typeCode = Phone.TYPE_HOME; + } else if (types.contains(TelephoneType.WORK)) { + typeCode = Phone.TYPE_WORK; + } else if (types.contains(Contact.PHONE_TYPE_CALLBACK)) { + typeCode = Phone.TYPE_CALLBACK; + } else if (types.contains(TelephoneType.CAR)) { + typeCode = Phone.TYPE_CAR; + } else if (types.contains(Contact.PHONE_TYPE_COMPANY_MAIN)) { + typeCode = Phone.TYPE_COMPANY_MAIN; + } else if (types.contains(TelephoneType.ISDN)) { + typeCode = Phone.TYPE_ISDN; + } else if (types.contains(TelephoneType.PREF)) { + typeCode = Phone.TYPE_MAIN; + } else if (types.contains(Contact.PHONE_TYPE_RADIO)) { + typeCode = Phone.TYPE_RADIO; + } else if (types.contains(TelephoneType.TEXTPHONE)) { + typeCode = Phone.TYPE_TELEX; + } else if (types.contains(TelephoneType.TEXT)) { + typeCode = Phone.TYPE_TTY_TDD; + } else if (types.contains(Contact.PHONE_TYPE_ASSISTANT)) { + typeCode = Phone.TYPE_ASSISTANT; + } else if (types.contains(Contact.PHONE_TYPE_MMS)) { + typeCode = Phone.TYPE_MMS; + } else if (!types.isEmpty()) { + TelephoneType type = types.iterator().next(); + typeCode = Phone.TYPE_CUSTOM; + typeLabel = xNameToLabel(type.getValue()); + } + + builder = builder + .withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE) + .withValue(Phone.NUMBER, number.getText()) + .withValue(Phone.TYPE, typeCode) + .withValue(Phone.IS_PRIMARY, is_primary ? 1 : 0) + .withValue(Phone.IS_SUPER_PRIMARY, is_primary ? 1 : 0); + if (typeLabel != null) + builder = builder.withValue(Phone.LABEL, typeLabel); + return builder; + } + + protected Builder buildEmail(Builder builder, ezvcard.property.Email email) { + int typeCode = 0; + String typeLabel = null; + boolean is_primary = false; + + for (EmailType type : email.getTypes()) + if (type == EmailType.PREF) + is_primary = true; + else if (type == EmailType.HOME) + typeCode = Email.TYPE_HOME; + else if (type == EmailType.WORK) + typeCode = Email.TYPE_WORK; + else if (type == Contact.EMAIL_TYPE_MOBILE) + typeCode = Email.TYPE_MOBILE; + if (typeCode == 0) { + if (email.getTypes().isEmpty()) + typeCode = Email.TYPE_OTHER; + else { + typeCode = Email.TYPE_CUSTOM; + typeLabel = xNameToLabel(email.getTypes().iterator().next().getValue()); + } + } + + builder = builder + .withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE) + .withValue(Email.ADDRESS, email.getValue()) + .withValue(Email.TYPE, typeCode) + .withValue(Email.IS_PRIMARY, is_primary ? 1 : 0) + .withValue(Phone.IS_SUPER_PRIMARY, is_primary ? 1 : 0);; + if (typeLabel != null) + builder = builder.withValue(Email.LABEL, typeLabel); + return builder; + } + + protected Builder buildPhoto(Builder builder, byte[] photo) { + return builder + .withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE) + .withValue(Photo.PHOTO, photo); + } + + protected Builder buildOrganization(Builder builder, Contact contact) { + if (contact.getOrganization() == null && contact.getJobTitle() == null && contact.getJobDescription() == null) + return null; + + ezvcard.property.Organization organization = contact.getOrganization(); + String company = null, department = null; + if (organization != null) { + Iterator org = organization.getValues().iterator(); + if (org.hasNext()) + company = org.next(); + if (org.hasNext()) + department = org.next(); + } + + return builder + .withValue(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE) + .withValue(Organization.COMPANY, company) + .withValue(Organization.DEPARTMENT, department) + .withValue(Organization.TITLE, contact.getJobTitle()) + .withValue(Organization.JOB_DESCRIPTION, contact.getJobDescription()); + } + + protected Builder buildIMPP(Builder builder, Impp impp) { + int typeCode = 0; + String typeLabel = null; + for (ImppType type : impp.getTypes()) + if (type == ImppType.HOME) + typeCode = Im.TYPE_HOME; + else if (type == ImppType.WORK || type == ImppType.BUSINESS) + typeCode = Im.TYPE_WORK; + if (typeCode == 0) + if (impp.getTypes().isEmpty()) + typeCode = Im.TYPE_OTHER; + else { + typeCode = Im.TYPE_CUSTOM; + typeLabel = xNameToLabel(impp.getTypes().iterator().next().getValue()); + } + + int protocolCode = 0; + String protocolLabel = null; + + String protocol = impp.getProtocol(); + if (protocol == null) { + Log.w(TAG, "Ignoring IMPP address without protocol"); + return null; + } + + // SIP addresses are IMPP entries in the VCard but locally stored in SipAddress rather than Im + boolean sipAddress = false; + + if (impp.isAim()) + protocolCode = Im.PROTOCOL_AIM; + else if (impp.isMsn()) + protocolCode = Im.PROTOCOL_MSN; + else if (impp.isYahoo()) + protocolCode = Im.PROTOCOL_YAHOO; + else if (impp.isSkype()) + protocolCode = Im.PROTOCOL_SKYPE; + else if (protocol.equalsIgnoreCase("qq")) + protocolCode = Im.PROTOCOL_QQ; + else if (protocol.equalsIgnoreCase("google-talk")) + protocolCode = Im.PROTOCOL_GOOGLE_TALK; + else if (impp.isIcq()) + protocolCode = Im.PROTOCOL_ICQ; + else if (impp.isXmpp() || protocol.equalsIgnoreCase("jabber")) + protocolCode = Im.PROTOCOL_JABBER; + else if (protocol.equalsIgnoreCase("netmeeting")) + protocolCode = Im.PROTOCOL_NETMEETING; + else if (protocol.equalsIgnoreCase("sip")) + sipAddress = true; + else { + protocolCode = Im.PROTOCOL_CUSTOM; + protocolLabel = protocol; + } + + if (sipAddress) + // save as SIP address + builder = builder + .withValue(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE) + .withValue(Im.DATA, impp.getHandle()) + .withValue(Im.TYPE, typeCode); + else { + // save as IM address + builder = builder + .withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE) + .withValue(Im.DATA, impp.getHandle()) + .withValue(Im.TYPE, typeCode) + .withValue(Im.PROTOCOL, protocolCode); + if (protocolLabel != null) + builder = builder.withValue(Im.CUSTOM_PROTOCOL, protocolLabel); + } + if (typeLabel != null) + builder = builder.withValue(Im.LABEL, typeLabel); + return builder; + } + + protected Builder buildNickName(Builder builder, String nickName) { + return builder + .withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE) + .withValue(Nickname.NAME, nickName); + } + + protected Builder buildNote(Builder builder, String note) { + return builder + .withValue(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE) + .withValue(Note.NOTE, note); + } + + protected Builder buildAddress(Builder builder, Address address) { + /* street po.box (extended) + * region + * postal code city + * country + */ + String formattedAddress = address.getLabel(); + if (StringUtils.isEmpty(formattedAddress)) { + String lineStreet = StringUtils.join(new String[] { address.getStreetAddress(), address.getPoBox(), address.getExtendedAddress() }, " "), + lineLocality = StringUtils.join(new String[] { address.getPostalCode(), address.getLocality() }, " "); + + List lines = new LinkedList(); + if (lineStreet != null) + lines.add(lineStreet); + if (address.getRegion() != null && !address.getRegion().isEmpty()) + lines.add(address.getRegion()); + if (lineLocality != null) + lines.add(lineLocality); + + formattedAddress = StringUtils.join(lines, "\n"); + } + + int typeCode = 0; + String typeLabel = null; + for (AddressType type : address.getTypes()) + if (type == AddressType.HOME) + typeCode = StructuredPostal.TYPE_HOME; + else if (type == AddressType.WORK) + typeCode = StructuredPostal.TYPE_WORK; + if (typeCode == 0) + if (address.getTypes().isEmpty()) + typeCode = StructuredPostal.TYPE_OTHER; + else { + typeCode = StructuredPostal.TYPE_CUSTOM; + typeLabel = xNameToLabel(address.getTypes().iterator().next().getValue()); + } + + builder = builder + .withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE) + .withValue(StructuredPostal.FORMATTED_ADDRESS, formattedAddress) + .withValue(StructuredPostal.TYPE, typeCode) + .withValue(StructuredPostal.STREET, address.getStreetAddress()) + .withValue(StructuredPostal.POBOX, address.getPoBox()) + .withValue(StructuredPostal.NEIGHBORHOOD, address.getExtendedAddress()) + .withValue(StructuredPostal.CITY, address.getLocality()) + .withValue(StructuredPostal.REGION, address.getRegion()) + .withValue(StructuredPostal.POSTCODE, address.getPostalCode()) + .withValue(StructuredPostal.COUNTRY, address.getCountry()); + if (typeLabel != null) + builder = builder.withValue(StructuredPostal.LABEL, typeLabel); + return builder; + } + + protected Builder buildGroupMembership(Builder builder, String group) { + return builder + .withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE) + .withValue(GroupMembership.GROUP_SOURCE_ID, group); + } + + protected Builder buildURL(Builder builder, String url) { + return builder + .withValue(Data.MIMETYPE, Website.CONTENT_ITEM_TYPE) + .withValue(Website.URL, url); + } + + protected Builder buildEvent(Builder builder, DateOrTimeProperty date, int type) { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US); + if (date.getDate() == null) { + Log.i(TAG, "Ignoring contact event without date"); + return null; + } + return builder + .withValue(Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE) + .withValue(CommonDataKinds.Event.TYPE, type) + .withValue(CommonDataKinds.Event.START_DATE, formatter.format(date.getDate())); + } + + + + /* helper methods */ + + protected Uri dataURI() { + return syncAdapterURI(Data.CONTENT_URI); + } + + protected static String labelToXName(String label) { + return "X-" + label.replaceAll(" ","_").replaceAll("[^\\p{L}\\p{Nd}\\-_]", "").toUpperCase(Locale.US); + } + + private Builder newDataInsertBuilder(long raw_contact_id, Integer backrefIdx) { + return newDataInsertBuilder(dataURI(), Data.RAW_CONTACT_ID, raw_contact_id, backrefIdx); + } + + protected static String xNameToLabel(String xname) { + // "X-MY_PROPERTY" + // 1. ensure lower case -> "x-my_property" + // 2. remove x- from beginning -> "my_property" + // 3. replace "_" by " " -> "my property" + // 4. capitalize -> "My Property" + String lowerCase = StringUtils.lowerCase(xname, Locale.US), + withoutPrefix = StringUtils.removeStart(lowerCase, "x-"), + withSpaces = StringUtils.replace(withoutPrefix, "_", " "); + return WordUtils.capitalize(withSpaces); + } + +} diff --git a/src/at/bitfire/davdroid/resource/LocalCalendar.java b/src/at/bitfire/davdroid/resource/LocalCalendar.java new file mode 100644 index 00000000..9fb60064 --- /dev/null +++ b/src/at/bitfire/davdroid/resource/LocalCalendar.java @@ -0,0 +1,612 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource; + +import java.net.URI; +import java.net.URISyntaxException; +import java.text.ParseException; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import lombok.Cleanup; +import lombok.Getter; +import net.fortuna.ical4j.model.Dur; +import net.fortuna.ical4j.model.Parameter; +import net.fortuna.ical4j.model.ParameterList; +import net.fortuna.ical4j.model.PropertyList; +import net.fortuna.ical4j.model.component.VAlarm; +import net.fortuna.ical4j.model.parameter.Cn; +import net.fortuna.ical4j.model.parameter.CuType; +import net.fortuna.ical4j.model.parameter.PartStat; +import net.fortuna.ical4j.model.parameter.Role; +import net.fortuna.ical4j.model.property.Action; +import net.fortuna.ical4j.model.property.Attendee; +import net.fortuna.ical4j.model.property.Description; +import net.fortuna.ical4j.model.property.Duration; +import net.fortuna.ical4j.model.property.ExDate; +import net.fortuna.ical4j.model.property.ExRule; +import net.fortuna.ical4j.model.property.Organizer; +import net.fortuna.ical4j.model.property.RDate; +import net.fortuna.ical4j.model.property.RRule; +import net.fortuna.ical4j.model.property.Status; + +import org.apache.commons.lang.StringUtils; + +import android.accounts.Account; +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.ContentProviderClient; +import android.content.ContentProviderOperation; +import android.content.ContentProviderOperation.Builder; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.net.Uri; +import android.os.Build; +import android.os.RemoteException; +import android.provider.CalendarContract; +import android.provider.CalendarContract.Attendees; +import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Events; +import android.provider.CalendarContract.Reminders; +import android.provider.ContactsContract; +import android.util.Log; + +/** + * Represents a locally stored calendar, containing Events. + * Communicates with the Android Contacts Provider which uses an SQLite + * database to store the contacts. + */ +public class LocalCalendar extends LocalCollection { + private static final String TAG = "davdroid.LocalCalendar"; + + @Getter protected long id; + @Getter protected String url; + + protected static String COLLECTION_COLUMN_CTAG = Calendars.CAL_SYNC1; + + + /* database fields */ + + @Override + protected Uri entriesURI() { + return syncAdapterURI(Events.CONTENT_URI); + } + + protected String entryColumnAccountType() { return Events.ACCOUNT_TYPE; } + protected String entryColumnAccountName() { return Events.ACCOUNT_NAME; } + + protected String entryColumnParentID() { return Events.CALENDAR_ID; } + protected String entryColumnID() { return Events._ID; } + protected String entryColumnRemoteName() { return Events._SYNC_ID; } + protected String entryColumnETag() { return Events.SYNC_DATA1; } + + protected String entryColumnDirty() { return Events.DIRTY; } + protected String entryColumnDeleted() { return Events.DELETED; } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + protected String entryColumnUID() { + return (android.os.Build.VERSION.SDK_INT >= 17) ? + Events.UID_2445 : Events.SYNC_DATA2; + } + + + /* class methods, constructor */ + + @SuppressLint("InlinedApi") + public static void create(Account account, ContentResolver resolver, ServerInfo.ResourceInfo info) throws LocalStorageException { + ContentProviderClient client = resolver.acquireContentProviderClient(CalendarContract.AUTHORITY); + if (client == null) + throw new LocalStorageException("No Calendar Provider found (Calendar app disabled?)"); + + int color = 0xFFC3EA6E; // fallback: "DAVdroid green" + if (info.getColor() != null) { + Pattern p = Pattern.compile("#?(\\p{XDigit}{6})(\\p{XDigit}{2})?"); + Matcher m = p.matcher(info.getColor()); + if (m.find()) { + int color_rgb = Integer.parseInt(m.group(1), 16); + int color_alpha = m.group(2) != null ? (Integer.parseInt(m.group(2), 16) & 0xFF) : 0xFF; + color = (color_alpha << 24) | color_rgb; + } + } + + ContentValues values = new ContentValues(); + values.put(Calendars.ACCOUNT_NAME, account.name); + values.put(Calendars.ACCOUNT_TYPE, account.type); + values.put(Calendars.NAME, info.getURL()); + values.put(Calendars.CALENDAR_DISPLAY_NAME, info.getTitle()); + values.put(Calendars.CALENDAR_COLOR, color); + values.put(Calendars.OWNER_ACCOUNT, account.name); + values.put(Calendars.SYNC_EVENTS, 1); + values.put(Calendars.VISIBLE, 1); + values.put(Calendars.ALLOWED_REMINDERS, Reminders.METHOD_ALERT); + + if (info.isReadOnly()) + values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_READ); + else { + values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_OWNER); + values.put(Calendars.CAN_ORGANIZER_RESPOND, 1); + values.put(Calendars.CAN_MODIFY_TIME_ZONE, 1); + } + + if (android.os.Build.VERSION.SDK_INT >= 15) { + values.put(Calendars.ALLOWED_AVAILABILITY, Events.AVAILABILITY_BUSY + "," + Events.AVAILABILITY_FREE + "," + Events.AVAILABILITY_TENTATIVE); + values.put(Calendars.ALLOWED_ATTENDEE_TYPES, Attendees.TYPE_NONE + "," + Attendees.TYPE_OPTIONAL + "," + Attendees.TYPE_REQUIRED + "," + Attendees.TYPE_RESOURCE); + } + + if (info.getTimezone() != null) + values.put(Calendars.CALENDAR_TIME_ZONE, info.getTimezone()); + + Log.i(TAG, "Inserting calendar: " + values.toString() + " -> " + calendarsURI(account).toString()); + try { + client.insert(calendarsURI(account), values); + } catch(RemoteException e) { + throw new LocalStorageException(e); + } + } + + public static LocalCalendar[] findAll(Account account, ContentProviderClient providerClient) throws RemoteException { + @Cleanup Cursor cursor = providerClient.query(calendarsURI(account), + new String[] { Calendars._ID, Calendars.NAME }, + Calendars.DELETED + "=0 AND " + Calendars.SYNC_EVENTS + "=1", null, null); + + LinkedList calendars = new LinkedList(); + while (cursor != null && cursor.moveToNext()) + calendars.add(new LocalCalendar(account, providerClient, cursor.getInt(0), cursor.getString(1))); + return calendars.toArray(new LocalCalendar[0]); + } + + public LocalCalendar(Account account, ContentProviderClient providerClient, long id, String url) throws RemoteException { + super(account, providerClient); + this.id = id; + this.url = url; + } + + + /* collection operations */ + + @Override + public String getCTag() throws LocalStorageException { + try { + @Cleanup Cursor c = providerClient.query(ContentUris.withAppendedId(calendarsURI(), id), + new String[] { COLLECTION_COLUMN_CTAG }, null, null, null); + if (c.moveToFirst()) { + return c.getString(0); + } else + throw new LocalStorageException("Couldn't query calendar CTag"); + } catch(RemoteException e) { + throw new LocalStorageException(e); + } + } + + @Override + public void setCTag(String cTag) throws LocalStorageException { + ContentValues values = new ContentValues(1); + values.put(COLLECTION_COLUMN_CTAG, cTag); + try { + providerClient.update(ContentUris.withAppendedId(calendarsURI(), id), values, null, null); + } catch(RemoteException e) { + throw new LocalStorageException(e); + } + } + + + /* create/update/delete */ + + public Event newResource(long localID, String resourceName, String eTag) { + return new Event(localID, resourceName, eTag); + } + + public void deleteAllExceptRemoteNames(Resource[] remoteResources) { + String where; + + if (remoteResources.length != 0) { + List sqlFileNames = new LinkedList(); + for (Resource res : remoteResources) + sqlFileNames.add(DatabaseUtils.sqlEscapeString(res.getName())); + where = entryColumnRemoteName() + " NOT IN (" + StringUtils.join(sqlFileNames, ",") + ")"; + } else + where = entryColumnRemoteName() + " IS NOT NULL"; + + Builder builder = ContentProviderOperation.newDelete(entriesURI()) + .withSelection(entryColumnParentID() + "=? AND (" + where + ")", new String[] { String.valueOf(id) }); + pendingOperations.add(builder + .withYieldAllowed(true) + .build()); + } + + + /* methods for populating the data object from the content provider */ + + + @Override + public void populate(Resource resource) throws LocalStorageException { + Event e = (Event)resource; + + try { + @Cleanup Cursor cursor = providerClient.query(ContentUris.withAppendedId(entriesURI(), e.getLocalID()), + new String[] { + /* 0 */ Events.TITLE, Events.EVENT_LOCATION, Events.DESCRIPTION, + /* 3 */ Events.DTSTART, Events.DTEND, Events.EVENT_TIMEZONE, Events.EVENT_END_TIMEZONE, Events.ALL_DAY, + /* 8 */ Events.STATUS, Events.ACCESS_LEVEL, + /* 10 */ Events.RRULE, Events.RDATE, Events.EXRULE, Events.EXDATE, + /* 14 */ Events.HAS_ATTENDEE_DATA, Events.ORGANIZER, Events.SELF_ATTENDEE_STATUS, + /* 17 */ entryColumnUID(), Events.DURATION, Events.AVAILABILITY + }, null, null, null); + if (cursor != null && cursor.moveToNext()) { + e.setUid(cursor.getString(17)); + + e.setSummary(cursor.getString(0)); + e.setLocation(cursor.getString(1)); + e.setDescription(cursor.getString(2)); + + boolean allDay = cursor.getInt(7) != 0; + long tsStart = cursor.getLong(3), + tsEnd = cursor.getLong(4); + String duration = cursor.getString(18); + + String tzId = null; + if (allDay) { + e.setDtStart(tsStart, null); + // provide only DTEND and not DURATION for all-day events + if (tsEnd == 0) { + Dur dur = new Dur(duration); + java.util.Date dEnd = dur.getTime(new java.util.Date(tsStart)); + tsEnd = dEnd.getTime(); + } + e.setDtEnd(tsEnd, null); + + } else { + // use the start time zone for the end time, too + // because apps like Samsung Planner allow the user to change "the" time zone but change the start time zone only + tzId = cursor.getString(5); + e.setDtStart(tsStart, tzId); + if (tsEnd != 0) + e.setDtEnd(tsEnd, tzId); + else if (!StringUtils.isEmpty(duration)) + e.setDuration(new Duration(new Dur(duration))); + } + + // recurrence + try { + String strRRule = cursor.getString(10); + if (!StringUtils.isEmpty(strRRule)) + e.setRrule(new RRule(strRRule)); + + String strRDate = cursor.getString(11); + if (!StringUtils.isEmpty(strRDate)) { + RDate rDate = new RDate(); + rDate.setValue(strRDate); + e.setRdate(rDate); + } + + String strExRule = cursor.getString(12); + if (!StringUtils.isEmpty(strExRule)) { + ExRule exRule = new ExRule(); + exRule.setValue(strExRule); + e.setExrule(exRule); + } + + String strExDate = cursor.getString(13); + if (!StringUtils.isEmpty(strExDate)) { + // ignored, see https://code.google.com/p/android/issues/detail?id=21426 + ExDate exDate = new ExDate(); + exDate.setValue(strExDate); + e.setExdate(exDate); + } + } catch (ParseException ex) { + Log.w(TAG, "Couldn't parse recurrence rules, ignoring", ex); + } catch (IllegalArgumentException ex) { + Log.w(TAG, "Invalid recurrence rules, ignoring", ex); + } + + // status + switch (cursor.getInt(8)) { + case Events.STATUS_CONFIRMED: + e.setStatus(Status.VEVENT_CONFIRMED); + break; + case Events.STATUS_TENTATIVE: + e.setStatus(Status.VEVENT_TENTATIVE); + break; + case Events.STATUS_CANCELED: + e.setStatus(Status.VEVENT_CANCELLED); + } + + // availability + e.setOpaque(cursor.getInt(19) != Events.AVAILABILITY_FREE); + + // attendees + if (cursor.getInt(14) != 0) { // has attendees + try { + e.setOrganizer(new Organizer(new URI("mailto", cursor.getString(15), null))); + } catch (URISyntaxException ex) { + Log.e(TAG, "Error when creating ORGANIZER URI, ignoring", ex); + } + populateAttendees(e); + } + + // classification + switch (cursor.getInt(9)) { + case Events.ACCESS_CONFIDENTIAL: + case Events.ACCESS_PRIVATE: + e.setForPublic(false); + break; + case Events.ACCESS_PUBLIC: + e.setForPublic(true); + } + + populateReminders(e); + } else + throw new RecordNotFoundException(); + } catch(RemoteException ex) { + throw new LocalStorageException(ex); + } + } + + + void populateAttendees(Event e) throws RemoteException { + Uri attendeesUri = Attendees.CONTENT_URI.buildUpon() + .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") + .build(); + @Cleanup Cursor c = providerClient.query(attendeesUri, new String[] { + /* 0 */ Attendees.ATTENDEE_EMAIL, Attendees.ATTENDEE_NAME, Attendees.ATTENDEE_TYPE, + /* 3 */ Attendees.ATTENDEE_RELATIONSHIP, Attendees.STATUS + }, Attendees.EVENT_ID + "=?", new String[] { String.valueOf(e.getLocalID()) }, null); + while (c != null && c.moveToNext()) { + try { + Attendee attendee = new Attendee(new URI("mailto", c.getString(0), null)); + ParameterList params = attendee.getParameters(); + + String cn = c.getString(1); + if (cn != null) + params.add(new Cn(cn)); + + // type + int type = c.getInt(2); + params.add((type == Attendees.TYPE_RESOURCE) ? CuType.RESOURCE : CuType.INDIVIDUAL); + + // role + int relationship = c.getInt(3); + switch (relationship) { + case Attendees.RELATIONSHIP_ORGANIZER: + params.add(Role.CHAIR); + break; + case Attendees.RELATIONSHIP_ATTENDEE: + case Attendees.RELATIONSHIP_PERFORMER: + case Attendees.RELATIONSHIP_SPEAKER: + params.add((type == Attendees.TYPE_REQUIRED) ? Role.REQ_PARTICIPANT : Role.OPT_PARTICIPANT); + break; + case Attendees.RELATIONSHIP_NONE: + params.add(Role.NON_PARTICIPANT); + } + + // status + switch (c.getInt(4)) { + case Attendees.ATTENDEE_STATUS_INVITED: + params.add(PartStat.NEEDS_ACTION); + break; + case Attendees.ATTENDEE_STATUS_ACCEPTED: + params.add(PartStat.ACCEPTED); + break; + case Attendees.ATTENDEE_STATUS_DECLINED: + params.add(PartStat.DECLINED); + break; + case Attendees.ATTENDEE_STATUS_TENTATIVE: + params.add(PartStat.TENTATIVE); + break; + } + + e.addAttendee(attendee); + } catch (URISyntaxException ex) { + Log.e(TAG, "Couldn't parse attendee information, ignoring", ex); + } + } + } + + void populateReminders(Event e) throws RemoteException { + // reminders + Uri remindersUri = Reminders.CONTENT_URI.buildUpon() + .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") + .build(); + @Cleanup Cursor c = providerClient.query(remindersUri, new String[] { + /* 0 */ Reminders.MINUTES, Reminders.METHOD + }, Reminders.EVENT_ID + "=?", new String[] { String.valueOf(e.getLocalID()) }, null); + while (c != null && c.moveToNext()) { + VAlarm alarm = new VAlarm(new Dur(0, 0, -c.getInt(0), 0)); + + PropertyList props = alarm.getProperties(); + switch (c.getInt(1)) { + /*case Reminders.METHOD_EMAIL: + props.add(Action.EMAIL); + break;*/ + default: + props.add(Action.DISPLAY); + props.add(new Description(e.getSummary())); + } + e.addAlarm(alarm); + } + } + + + /* content builder methods */ + + @Override + protected Builder buildEntry(Builder builder, Resource resource) { + Event event = (Event)resource; + + builder = builder + .withValue(Events.CALENDAR_ID, id) + .withValue(entryColumnRemoteName(), event.getName()) + .withValue(entryColumnETag(), event.getETag()) + .withValue(entryColumnUID(), event.getUid()) + .withValue(Events.ALL_DAY, event.isAllDay() ? 1 : 0) + .withValue(Events.DTSTART, event.getDtStartInMillis()) + .withValue(Events.EVENT_TIMEZONE, event.getDtStartTzID()) + .withValue(Events.HAS_ATTENDEE_DATA, event.getAttendees().isEmpty() ? 0 : 1) + .withValue(Events.GUESTS_CAN_INVITE_OTHERS, 1) + .withValue(Events.GUESTS_CAN_MODIFY, 1) + .withValue(Events.GUESTS_CAN_SEE_GUESTS, 1); + + boolean recurring = false; + if (event.getRrule() != null) { + recurring = true; + builder = builder.withValue(Events.RRULE, event.getRrule().getValue()); + } + if (event.getRdate() != null) { + recurring = true; + builder = builder.withValue(Events.RDATE, event.getRdate().getValue()); + } + if (event.getExrule() != null) + builder = builder.withValue(Events.EXRULE, event.getExrule().getValue()); + if (event.getExdate() != null) + builder = builder.withValue(Events.EXDATE, event.getExdate().getValue()); + + // set either DTEND for single-time events or DURATION for recurring events + // because that's the way Android likes it (see docs) + if (recurring) { + // calculate DURATION from start and end date + Duration duration = new Duration(event.getDtStart().getDate(), event.getDtEnd().getDate()); + builder = builder.withValue(Events.DURATION, duration.getValue()); + } else { + builder = builder + .withValue(Events.DTEND, event.getDtEndInMillis()) + .withValue(Events.EVENT_END_TIMEZONE, event.getDtEndTzID()); + } + + if (event.getSummary() != null) + builder = builder.withValue(Events.TITLE, event.getSummary()); + if (event.getLocation() != null) + builder = builder.withValue(Events.EVENT_LOCATION, event.getLocation()); + if (event.getDescription() != null) + builder = builder.withValue(Events.DESCRIPTION, event.getDescription()); + + if (event.getOrganizer() != null && event.getOrganizer().getCalAddress() != null) { + URI organizer = event.getOrganizer().getCalAddress(); + if (organizer.getScheme() != null && organizer.getScheme().equalsIgnoreCase("mailto")) + builder = builder.withValue(Events.ORGANIZER, organizer.getSchemeSpecificPart()); + } + + Status status = event.getStatus(); + if (status != null) { + int statusCode = Events.STATUS_TENTATIVE; + if (status == Status.VEVENT_CONFIRMED) + statusCode = Events.STATUS_CONFIRMED; + else if (status == Status.VEVENT_CANCELLED) + statusCode = Events.STATUS_CANCELED; + builder = builder.withValue(Events.STATUS, statusCode); + } + + builder = builder.withValue(Events.AVAILABILITY, event.isOpaque() ? Events.AVAILABILITY_BUSY : Events.AVAILABILITY_FREE); + + if (event.getForPublic() != null) + builder = builder.withValue(Events.ACCESS_LEVEL, event.getForPublic() ? Events.ACCESS_PUBLIC : Events.ACCESS_PRIVATE); + + return builder; + } + + + @Override + protected void addDataRows(Resource resource, long localID, int backrefIdx) { + Event event = (Event)resource; + for (Attendee attendee : event.getAttendees()) + pendingOperations.add(buildAttendee(newDataInsertBuilder(Attendees.CONTENT_URI, Attendees.EVENT_ID, localID, backrefIdx), attendee).build()); + for (VAlarm alarm : event.getAlarms()) + pendingOperations.add(buildReminder(newDataInsertBuilder(Reminders.CONTENT_URI, Reminders.EVENT_ID, localID, backrefIdx), alarm).build()); + } + + @Override + protected void removeDataRows(Resource resource) { + Event event = (Event)resource; + pendingOperations.add(ContentProviderOperation.newDelete(syncAdapterURI(Attendees.CONTENT_URI)) + .withSelection(Attendees.EVENT_ID + "=?", + new String[] { String.valueOf(event.getLocalID()) }).build()); + pendingOperations.add(ContentProviderOperation.newDelete(syncAdapterURI(Reminders.CONTENT_URI)) + .withSelection(Reminders.EVENT_ID + "=?", + new String[] { String.valueOf(event.getLocalID()) }).build()); + } + + + @SuppressLint("InlinedApi") + protected Builder buildAttendee(Builder builder, Attendee attendee) { + Uri member = Uri.parse(attendee.getValue()); + String email = member.getSchemeSpecificPart(); + + Cn cn = (Cn)attendee.getParameter(Parameter.CN); + if (cn != null) + builder = builder.withValue(Attendees.ATTENDEE_NAME, cn.getValue()); + + int type = Attendees.TYPE_NONE; + + CuType cutype = (CuType)attendee.getParameter(Parameter.CUTYPE); + if (cutype == CuType.RESOURCE) + type = Attendees.TYPE_RESOURCE; + else { + Role role = (Role)attendee.getParameter(Parameter.ROLE); + int relationship; + if (role == Role.CHAIR) + relationship = Attendees.RELATIONSHIP_ORGANIZER; + else { + relationship = Attendees.RELATIONSHIP_ATTENDEE; + if (role == Role.OPT_PARTICIPANT) + type = Attendees.TYPE_OPTIONAL; + else if (role == Role.REQ_PARTICIPANT) + type = Attendees.TYPE_REQUIRED; + } + builder = builder.withValue(Attendees.ATTENDEE_RELATIONSHIP, relationship); + } + + int status = Attendees.ATTENDEE_STATUS_NONE; + PartStat partStat = (PartStat)attendee.getParameter(Parameter.PARTSTAT); + if (partStat == null || partStat == PartStat.NEEDS_ACTION) + status = Attendees.ATTENDEE_STATUS_INVITED; + else if (partStat == PartStat.ACCEPTED) + status = Attendees.ATTENDEE_STATUS_ACCEPTED; + else if (partStat == PartStat.DECLINED) + status = Attendees.ATTENDEE_STATUS_DECLINED; + else if (partStat == PartStat.TENTATIVE) + status = Attendees.ATTENDEE_STATUS_TENTATIVE; + + return builder + .withValue(Attendees.ATTENDEE_EMAIL, email) + .withValue(Attendees.ATTENDEE_TYPE, type) + .withValue(Attendees.ATTENDEE_STATUS, status); + } + + protected Builder buildReminder(Builder builder, VAlarm alarm) { + int minutes = 0; + + Dur duration; + if (alarm.getTrigger() != null && (duration = alarm.getTrigger().getDuration()) != null) + minutes = duration.getDays() * 24*60 + duration.getHours()*60 + duration.getMinutes(); + + Log.d(TAG, "Adding alarm " + minutes + " min before"); + + return builder + .withValue(Reminders.METHOD, Reminders.METHOD_ALERT) + .withValue(Reminders.MINUTES, minutes); + } + + + + /* private helper methods */ + + protected static Uri calendarsURI(Account account) { + return Calendars.CONTENT_URI.buildUpon().appendQueryParameter(Calendars.ACCOUNT_NAME, account.name) + .appendQueryParameter(Calendars.ACCOUNT_TYPE, account.type) + .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true").build(); + } + + protected Uri calendarsURI() { + return calendarsURI(account); + } + +} diff --git a/src/at/bitfire/davdroid/resource/LocalCollection.java b/src/at/bitfire/davdroid/resource/LocalCollection.java new file mode 100644 index 00000000..24293421 --- /dev/null +++ b/src/at/bitfire/davdroid/resource/LocalCollection.java @@ -0,0 +1,361 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource; + +import java.util.ArrayList; + +import lombok.Cleanup; +import android.accounts.Account; +import android.content.ContentProviderClient; +import android.content.ContentProviderOperation; +import android.content.ContentProviderOperation.Builder; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.OperationApplicationException; +import android.database.Cursor; +import android.net.Uri; +import android.os.RemoteException; +import android.provider.CalendarContract; +import android.util.Log; + +/** + * Represents a locally-stored synchronizable collection (for instance, the + * address book or a calendar). Manages a CTag that stores the last known + * remote CTag (the remote CTag changes whenever something in the remote collection changes). + * + * @param Subtype of Resource that can be stored in the collection + */ +public abstract class LocalCollection { + private static final String TAG = "davdroid.LocalCollection"; + + protected Account account; + protected ContentProviderClient providerClient; + protected ArrayList pendingOperations = new ArrayList(); + + + // database fields + + /** base Uri of the collection's entries (for instance, Events.CONTENT_URI); + * apply syncAdapterURI() before returning a value */ + abstract protected Uri entriesURI(); + + /** column name of the type of the account the entry belongs to */ + abstract protected String entryColumnAccountType(); + /** column name of the name of the account the entry belongs to */ + abstract protected String entryColumnAccountName(); + + /** column name of the collection ID the entry belongs to */ + abstract protected String entryColumnParentID(); + /** column name of an entry's ID */ + abstract protected String entryColumnID(); + /** column name of an entry's file name on the WebDAV server */ + abstract protected String entryColumnRemoteName(); + /** column name of an entry's last ETag on the WebDAV server; null if entry hasn't been uploaded yet */ + abstract protected String entryColumnETag(); + + /** column name of an entry's "dirty" flag (managed by content provider) */ + abstract protected String entryColumnDirty(); + /** column name of an entry's "deleted" flag (managed by content provider) */ + abstract protected String entryColumnDeleted(); + + /** column name of an entry's UID */ + abstract protected String entryColumnUID(); + + + LocalCollection(Account account, ContentProviderClient providerClient) { + this.account = account; + this.providerClient = providerClient; + } + + + // collection operations + + /** gets the ID if the collection (for instance, ID of the Android calendar) */ + abstract public long getId(); + /** gets the CTag of the collection */ + abstract public String getCTag() throws LocalStorageException; + /** sets the CTag of the collection */ + abstract public void setCTag(String cTag) throws LocalStorageException; + + + // content provider (= database) querying + + /** + * Finds new resources (resources which haven't been uploaded yet). + * New resources are 1) dirty, and 2) don't have an ETag yet. + * + * @return IDs of new resources + * @throws LocalStorageException when the content provider couldn't be queried + */ + public long[] findNew() throws LocalStorageException { + String where = entryColumnDirty() + "=1 AND " + entryColumnETag() + " IS NULL"; + if (entryColumnParentID() != null) + where += " AND " + entryColumnParentID() + "=" + String.valueOf(getId()); + try { + @Cleanup Cursor cursor = providerClient.query(entriesURI(), + new String[] { entryColumnID() }, + where, null, null); + if (cursor == null) + throw new LocalStorageException("Couldn't query new records"); + + long[] fresh = new long[cursor.getCount()]; + for (int idx = 0; cursor.moveToNext(); idx++) { + long id = cursor.getLong(0); + + // new record: generate UID + remote file name so that we can upload + T resource = findById(id, false); + resource.initialize(); + // write generated UID + remote file name into database + ContentValues values = new ContentValues(2); + values.put(entryColumnUID(), resource.getUid()); + values.put(entryColumnRemoteName(), resource.getName()); + providerClient.update(ContentUris.withAppendedId(entriesURI(), id), values, null, null); + + fresh[idx] = id; + } + return fresh; + } catch(RemoteException ex) { + throw new LocalStorageException(ex); + } + } + + /** + * Finds updated resources (resources which have already been uploaded, but have changed locally). + * Updated resources are 1) dirty, and 2) already have an ETag. + * + * @return IDs of updated resources + * @throws LocalStorageException when the content provider couldn't be queried + */ + public long[] findUpdated() throws LocalStorageException { + String where = entryColumnDirty() + "=1 AND " + entryColumnETag() + " IS NOT NULL"; + if (entryColumnParentID() != null) + where += " AND " + entryColumnParentID() + "=" + String.valueOf(getId()); + try { + @Cleanup Cursor cursor = providerClient.query(entriesURI(), + new String[] { entryColumnID(), entryColumnRemoteName(), entryColumnETag() }, + where, null, null); + if (cursor == null) + throw new LocalStorageException("Couldn't query updated records"); + + long[] updated = new long[cursor.getCount()]; + for (int idx = 0; cursor.moveToNext(); idx++) + updated[idx] = cursor.getLong(0); + return updated; + } catch(RemoteException ex) { + throw new LocalStorageException(ex); + } + } + + /** + * Finds deleted resources (resources which have been marked for deletion). + * Deleted resources have the "deleted" flag set. + * + * @return IDs of deleted resources + * @throws LocalStorageException when the content provider couldn't be queried + */ + public long[] findDeleted() throws LocalStorageException { + String where = entryColumnDeleted() + "=1"; + if (entryColumnParentID() != null) + where += " AND " + entryColumnParentID() + "=" + String.valueOf(getId()); + try { + @Cleanup Cursor cursor = providerClient.query(entriesURI(), + new String[] { entryColumnID(), entryColumnRemoteName(), entryColumnETag() }, + where, null, null); + if (cursor == null) + throw new LocalStorageException("Couldn't query dirty records"); + + long deleted[] = new long[cursor.getCount()]; + for (int idx = 0; cursor.moveToNext(); idx++) + deleted[idx] = cursor.getLong(0); + return deleted; + } catch(RemoteException ex) { + throw new LocalStorageException(ex); + } + } + + /** + * Finds a specific resource by ID. + * @param localID ID of the resource + * @param populate true: populates all data fields (for instance, contact or event details); + * false: only remote file name and ETag are populated + * @return resource with either ID/remote file/name/ETag or all fields populated + * @throws RecordNotFoundException when the resource couldn't be found + * @throws LocalStorageException when the content provider couldn't be queried + */ + public T findById(long localID, boolean populate) throws LocalStorageException { + try { + @Cleanup Cursor cursor = providerClient.query(ContentUris.withAppendedId(entriesURI(), localID), + new String[] { entryColumnRemoteName(), entryColumnETag() }, null, null, null); + if (cursor != null && cursor.moveToNext()) { + T resource = newResource(localID, cursor.getString(0), cursor.getString(1)); + if (populate) + populate(resource); + return resource; + } else + throw new RecordNotFoundException(); + } catch(RemoteException ex) { + throw new LocalStorageException(ex); + } + } + + /** + * Finds a specific resource by remote file name. + * @param localID remote file name of the resource + * @param populate true: populates all data fields (for instance, contact or event details); + * false: only remote file name and ETag are populated + * @return resource with either ID/remote file/name/ETag or all fields populated + * @throws RecordNotFoundException when the resource couldn't be found + * @throws LocalStorageException when the content provider couldn't be queried + */ + public T findByRemoteName(String remoteName, boolean populate) throws LocalStorageException { + try { + @Cleanup Cursor cursor = providerClient.query(entriesURI(), + new String[] { entryColumnID(), entryColumnRemoteName(), entryColumnETag() }, + entryColumnRemoteName() + "=?", new String[] { remoteName }, null); + if (cursor != null && cursor.moveToNext()) { + T resource = newResource(cursor.getLong(0), cursor.getString(1), cursor.getString(2)); + if (populate) + populate(resource); + return resource; + } else + throw new RecordNotFoundException(); + } catch(RemoteException ex) { + throw new LocalStorageException(ex); + } + } + + /** populates all data fields from the content provider */ + public abstract void populate(Resource record) throws LocalStorageException; + + + // create/update/delete + + /** + * Creates a new resource object in memory. No content provider operations involved. + * @param localID the ID of the resource + * @param resourceName the (remote) file name of the resource + * @param ETag of the resource + * @return the new resource object */ + abstract public T newResource(long localID, String resourceName, String eTag); + + /** Enqueues adding the resource (including all data) to the local collection. Requires commit(). */ + public void add(Resource resource) { + int idx = pendingOperations.size(); + pendingOperations.add( + buildEntry(ContentProviderOperation.newInsert(entriesURI()), resource) + .withYieldAllowed(true) + .build()); + + addDataRows(resource, -1, idx); + } + + /** Enqueues updating an existing resource in the local collection. The resource will be found by + * the remote file name and all data will be updated. Requires commit(). */ + public void updateByRemoteName(Resource remoteResource) throws LocalStorageException { + T localResource = findByRemoteName(remoteResource.getName(), false); + pendingOperations.add( + buildEntry(ContentProviderOperation.newUpdate(ContentUris.withAppendedId(entriesURI(), localResource.getLocalID())), remoteResource) + .withValue(entryColumnETag(), remoteResource.getETag()) + .withYieldAllowed(true) + .build()); + + removeDataRows(localResource); + addDataRows(remoteResource, localResource.getLocalID(), -1); + } + + /** Enqueues deleting a resource from the local collection. Requires commit(). */ + public void delete(Resource resource) { + pendingOperations.add(ContentProviderOperation + .newDelete(ContentUris.withAppendedId(entriesURI(), resource.getLocalID())) + .withYieldAllowed(true) + .build()); + } + + /** + * Enqueues deleting all resources except the give ones from the local collection. Requires commit(). + * @param remoteResources resources with these remote file names will be kept + */ + public abstract void deleteAllExceptRemoteNames(Resource[] remoteResources); + + /** Updates the locally-known ETag of a resource. */ + public void updateETag(Resource res, String eTag) throws LocalStorageException { + Log.d(TAG, "Setting ETag of local resource " + res + " to " + eTag); + + ContentValues values = new ContentValues(1); + values.put(entryColumnETag(), eTag); + try { + providerClient.update(ContentUris.withAppendedId(entriesURI(), res.getLocalID()), values, null, new String[] {}); + } catch (RemoteException e) { + throw new LocalStorageException(e); + } + } + + /** Enqueues removing the dirty flag from a locally-stored resource. Requires commit(). */ + public void clearDirty(Resource resource) { + pendingOperations.add(ContentProviderOperation + .newUpdate(ContentUris.withAppendedId(entriesURI(), resource.getLocalID())) + .withValue(entryColumnDirty(), 0) + .build()); + } + + /** Commits enqueued operations to the content provider (for batch operations). */ + public void commit() throws LocalStorageException { + if (!pendingOperations.isEmpty()) + try { + Log.d(TAG, "Committing " + pendingOperations.size() + " operations"); + providerClient.applyBatch(pendingOperations); + pendingOperations.clear(); + } catch (RemoteException ex) { + throw new LocalStorageException(ex); + } catch(OperationApplicationException ex) { + throw new LocalStorageException(ex); + } + } + + + // helpers + + protected void queueOperation(Builder builder) { + if (builder != null) + pendingOperations.add(builder.build()); + } + + /** Appends account type, name and CALLER_IS_SYNCADAPTER to an Uri. */ + protected Uri syncAdapterURI(Uri baseURI) { + return baseURI.buildUpon() + .appendQueryParameter(entryColumnAccountType(), account.type) + .appendQueryParameter(entryColumnAccountName(), account.name) + .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true") + .build(); + } + + protected Builder newDataInsertBuilder(Uri dataUri, String refFieldName, long raw_ref_id, Integer backrefIdx) { + Builder builder = ContentProviderOperation.newInsert(syncAdapterURI(dataUri)); + if (backrefIdx != -1) + return builder.withValueBackReference(refFieldName, backrefIdx); + else + return builder.withValue(refFieldName, raw_ref_id); + } + + + // content builders + + /** + * Builds the main entry (for instance, a ContactsContract.RawContacts row) from a resource. + * The entry is built for insertion to the location identified by entriesURI(). + * + * @param builder Builder to be extended by all resource data that can be stored without extra data rows. + */ + protected abstract Builder buildEntry(Builder builder, Resource resource); + + /** Enqueues adding extra data rows of the resource to the local collection. */ + protected abstract void addDataRows(Resource resource, long localID, int backrefIdx); + + /** Enqueues removing all extra data rows of the resource from the local collection. */ + protected abstract void removeDataRows(Resource resource); +} diff --git a/src/at/bitfire/davdroid/resource/LocalStorageException.java b/src/at/bitfire/davdroid/resource/LocalStorageException.java new file mode 100644 index 00000000..d33ad46a --- /dev/null +++ b/src/at/bitfire/davdroid/resource/LocalStorageException.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource; + +public class LocalStorageException extends Exception { + private static final long serialVersionUID = -7787658815291629529L; + + private static final String detailMessage = "Couldn't access local content provider"; + + + public LocalStorageException(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + } + + public LocalStorageException(String detailMessage) { + super(detailMessage); + } + + public LocalStorageException(Throwable throwable) { + super(detailMessage, throwable); + } + + public LocalStorageException() { + super(detailMessage); + } +} diff --git a/src/at/bitfire/davdroid/resource/RecordNotFoundException.java b/src/at/bitfire/davdroid/resource/RecordNotFoundException.java new file mode 100644 index 00000000..98daa77c --- /dev/null +++ b/src/at/bitfire/davdroid/resource/RecordNotFoundException.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource; + +/** + * Thrown when a local record (for instance, Contact with ID 12345) should be read + * but could not be found. + */ +public class RecordNotFoundException extends LocalStorageException { + private static final long serialVersionUID = 4961024282198632578L; + + private static final String detailMessage = "Record not found in local content provider"; + + + RecordNotFoundException(Throwable ex) { + super(detailMessage, ex); + } + + RecordNotFoundException() { + super(detailMessage); + } + +} diff --git a/src/at/bitfire/davdroid/resource/RemoteCollection.java b/src/at/bitfire/davdroid/resource/RemoteCollection.java new file mode 100644 index 00000000..d287e823 --- /dev/null +++ b/src/at/bitfire/davdroid/resource/RemoteCollection.java @@ -0,0 +1,181 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.LinkedList; +import java.util.List; + +import lombok.Cleanup; +import lombok.Getter; +import net.fortuna.ical4j.model.ValidationException; +import android.util.Log; +import at.bitfire.davdroid.webdav.DavException; +import at.bitfire.davdroid.webdav.DavMultiget; +import at.bitfire.davdroid.webdav.DavNoContentException; +import at.bitfire.davdroid.webdav.HttpException; +import at.bitfire.davdroid.webdav.HttpPropfind; +import at.bitfire.davdroid.webdav.WebDavResource; +import at.bitfire.davdroid.webdav.WebDavResource.PutMode; +import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient; +import ezvcard.io.text.VCardParseException; + +/** + * Represents a remotely stored synchronizable collection (collection as in + * WebDAV terminology). + * + * @param Subtype of Resource that can be stored in the collection + */ +public abstract class RemoteCollection { + private static final String TAG = "davdroid.RemoteCollection"; + + CloseableHttpClient httpClient; + @Getter WebDavResource collection; + + abstract protected String memberContentType(); + abstract protected DavMultiget.Type multiGetType(); + abstract protected T newResourceSkeleton(String name, String ETag); + + public RemoteCollection(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws MalformedURLException { + this.httpClient = httpClient; + + collection = new WebDavResource(httpClient, new URL(baseURL), user, password, preemptiveAuth); + } + + + /* collection operations */ + + public String getCTag() throws URISyntaxException, IOException, HttpException { + try { + if (collection.getCTag() == null && collection.getMembers() == null) // not already fetched + collection.propfind(HttpPropfind.Mode.COLLECTION_CTAG); + } catch (DavException e) { + return null; + } + return collection.getCTag(); + } + + public Resource[] getMemberETags() throws URISyntaxException, IOException, DavException, HttpException { + collection.propfind(HttpPropfind.Mode.MEMBERS_ETAG); + + List resources = new LinkedList(); + if (collection.getMembers() != null) { + for (WebDavResource member : collection.getMembers()) + resources.add(newResourceSkeleton(member.getName(), member.getETag())); + } + return resources.toArray(new Resource[0]); + } + + @SuppressWarnings("unchecked") + public Resource[] multiGet(Resource[] resources) throws URISyntaxException, IOException, DavException, HttpException { + try { + if (resources.length == 1) + return (T[]) new Resource[] { get(resources[0]) }; + + Log.i(TAG, "Multi-getting " + resources.length + " remote resource(s)"); + + LinkedList names = new LinkedList(); + for (Resource resource : resources) + names.add(resource.getName()); + + LinkedList foundResources = new LinkedList(); + collection.multiGet(multiGetType(), names.toArray(new String[0])); + if (collection.getMembers() == null) + throw new DavNoContentException(); + + for (WebDavResource member : collection.getMembers()) { + T resource = newResourceSkeleton(member.getName(), member.getETag()); + try { + if (member.getContent() != null) { + @Cleanup InputStream is = new ByteArrayInputStream(member.getContent()); + resource.parseEntity(is); + foundResources.add(resource); + } else + Log.e(TAG, "Ignoring entity without content"); + } catch (InvalidResourceException e) { + Log.e(TAG, "Ignoring unparseable entity in multi-response", e); + } + } + + return foundResources.toArray(new Resource[0]); + } catch (InvalidResourceException e) { + Log.e(TAG, "Couldn't parse entity from GET", e); + } + + return new Resource[0]; + } + + + /* internal member operations */ + + public Resource get(Resource resource) throws URISyntaxException, IOException, HttpException, DavException, InvalidResourceException { + WebDavResource member = new WebDavResource(collection, resource.getName()); + + if (resource instanceof Contact) + member.get(Contact.MIME_TYPE); + else if (resource instanceof Event) + member.get(Event.MIME_TYPE); + else { + Log.wtf(TAG, "Should fetch something, but neither contact nor calendar"); + throw new InvalidResourceException("Didn't now which MIME type to accept"); + } + + byte[] data = member.getContent(); + if (data == null) + throw new DavNoContentException(); + + @Cleanup InputStream is = new ByteArrayInputStream(data); + try { + resource.parseEntity(is); + } catch(VCardParseException e) { + throw new InvalidResourceException(e); + } + return resource; + } + + // returns ETag of the created resource, if returned by server + public String add(Resource res) throws URISyntaxException, IOException, HttpException, ValidationException { + WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag()); + member.setContentType(memberContentType()); + + @Cleanup ByteArrayOutputStream os = res.toEntity(); + String eTag = member.put(os.toByteArray(), PutMode.ADD_DONT_OVERWRITE); + + // after a successful upload, the collection has implicitely changed, too + collection.invalidateCTag(); + + return eTag; + } + + public void delete(Resource res) throws URISyntaxException, IOException, HttpException { + WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag()); + member.delete(); + + collection.invalidateCTag(); + } + + // returns ETag of the updated resource, if returned by server + public String update(Resource res) throws URISyntaxException, IOException, HttpException, ValidationException { + WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag()); + member.setContentType(memberContentType()); + + @Cleanup ByteArrayOutputStream os = res.toEntity(); + String eTag = member.put(os.toByteArray(), PutMode.UPDATE_DONT_OVERWRITE); + + // after a successful upload, the collection has implicitely changed, too + collection.invalidateCTag(); + + return eTag; + } +} diff --git a/src/at/bitfire/davdroid/resource/Resource.java b/src/at/bitfire/davdroid/resource/Resource.java new file mode 100644 index 00000000..bda3e2e5 --- /dev/null +++ b/src/at/bitfire/davdroid/resource/Resource.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * Represents a resource that can be contained in a LocalCollection or RemoteCollection + * for synchronization by WebDAV. + */ +@ToString +public abstract class Resource { + @Getter @Setter protected String name, ETag; + @Getter @Setter protected String uid; + @Getter protected long localID; + + + public Resource(String name, String ETag) { + this.name = name; + this.ETag = ETag; + } + + public Resource(long localID, String name, String ETag) { + this(name, ETag); + this.localID = localID; + } + + /** initializes UID and remote file name (required for first upload) */ + public abstract void initialize(); + + /** fills the resource data from an input stream (for instance, .vcf file for Contact) */ + public abstract void parseEntity(InputStream entity) throws IOException, InvalidResourceException; + /** writes the resource data to an output stream (for instance, .vcf file for Contact) */ + public abstract ByteArrayOutputStream toEntity() throws IOException; +} diff --git a/src/at/bitfire/davdroid/resource/ServerInfo.java b/src/at/bitfire/davdroid/resource/ServerInfo.java new file mode 100644 index 00000000..14f5b6f7 --- /dev/null +++ b/src/at/bitfire/davdroid/resource/ServerInfo.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource; + +import java.io.Serializable; +import java.net.URI; +import java.util.LinkedList; +import java.util.List; + +import ezvcard.VCardVersion; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor(suppressConstructorProperties=true) +@Data +public class ServerInfo implements Serializable { + private static final long serialVersionUID = 6744847358282980437L; + + enum Scheme { + HTTP, HTTPS, MAILTO + } + + final private URI baseURI; + final private String userName, password; + final boolean authPreemptive; + + private String errorMessage; + + private boolean calDAV = false, cardDAV = false; + private List + addressBooks = new LinkedList(), + calendars = new LinkedList(); + + + public boolean hasEnabledCalendars() { + for (ResourceInfo calendar : calendars) + if (calendar.enabled) + return true; + return false; + } + + + @RequiredArgsConstructor(suppressConstructorProperties=true) + @Data + public static class ResourceInfo implements Serializable { + private static final long serialVersionUID = -5516934508229552112L; + + public enum Type { + ADDRESS_BOOK, + CALENDAR + } + + boolean enabled = false; + + final Type type; + final boolean readOnly; + final String URL, title, description, color; + + VCardVersion vCardVersion; + + String timezone; + } +} diff --git a/src/at/bitfire/davdroid/syncadapter/AccountAuthenticatorService.java b/src/at/bitfire/davdroid/syncadapter/AccountAuthenticatorService.java new file mode 100644 index 00000000..f78cc4f6 --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/AccountAuthenticatorService.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import android.accounts.AbstractAccountAuthenticator; +import android.accounts.Account; +import android.accounts.AccountAuthenticatorResponse; +import android.accounts.AccountManager; +import android.accounts.NetworkErrorException; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; + +public class AccountAuthenticatorService extends Service { + private static AccountAuthenticator accountAuthenticator; + + private AccountAuthenticator getAuthenticator() { + if (accountAuthenticator != null) + return accountAuthenticator; + return accountAuthenticator = new AccountAuthenticator(this); + } + + @Override + public IBinder onBind(Intent intent) { + if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) + return getAuthenticator().getIBinder(); + return null; + } + + + private static class AccountAuthenticator extends AbstractAccountAuthenticator { + Context context; + + public AccountAuthenticator(Context context) { + super(context); + this.context = context; + } + + @Override + public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, + String[] requiredFeatures, Bundle options) throws NetworkErrorException { + Intent intent = new Intent(context, AddAccountActivity.class); + intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); + Bundle bundle = new Bundle(); + bundle.putParcelable(AccountManager.KEY_INTENT, intent); + return bundle; + } + + @Override + public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException { + return null; + } + + @Override + public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) { + return null; + } + + @Override + public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { + return null; + } + + @Override + public String getAuthTokenLabel(String authTokenType) { + return null; + } + + @Override + public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException { + return null; + } + + @Override + public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { + return null; + } + } +} diff --git a/src/at/bitfire/davdroid/syncadapter/AccountDetailsFragment.java b/src/at/bitfire/davdroid/syncadapter/AccountDetailsFragment.java new file mode 100644 index 00000000..37ab4734 --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/AccountDetailsFragment.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.Fragment; +import android.content.ContentResolver; +import android.os.Bundle; +import android.provider.CalendarContract; +import android.provider.ContactsContract; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; +import at.bitfire.davdroid.Constants; +import at.bitfire.davdroid.R; +import at.bitfire.davdroid.resource.LocalCalendar; +import at.bitfire.davdroid.resource.LocalStorageException; +import at.bitfire.davdroid.resource.ServerInfo; + +public class AccountDetailsFragment extends Fragment implements TextWatcher { + public static final String KEY_SERVER_INFO = "server_info"; + + ServerInfo serverInfo; + + EditText editAccountName; + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.account_details, container, false); + + serverInfo = (ServerInfo)getArguments().getSerializable(KEY_SERVER_INFO); + + editAccountName = (EditText)v.findViewById(R.id.account_name); + editAccountName.addTextChangedListener(this); + editAccountName.setText(serverInfo.getUserName()); + + TextView textAccountNameInfo = (TextView)v.findViewById(R.id.account_name_info); + if (!serverInfo.hasEnabledCalendars()) + textAccountNameInfo.setVisibility(View.GONE); + + setHasOptionsMenu(true); + return v; + } + + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.account_details, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.add_account: + addAccount(); + break; + default: + return false; + } + return true; + } + + + // actions + + void addAccount() { + ServerInfo serverInfo = (ServerInfo)getArguments().getSerializable(KEY_SERVER_INFO); + String accountName = editAccountName.getText().toString(); + + AccountManager accountManager = AccountManager.get(getActivity()); + Account account = new Account(accountName, Constants.ACCOUNT_TYPE); + Bundle userData = AccountSettings.createBundle(serverInfo); + + boolean syncContacts = false; + for (ServerInfo.ResourceInfo addressBook : serverInfo.getAddressBooks()) + if (addressBook.isEnabled()) { + ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1); + syncContacts = true; + continue; + } + if (syncContacts) { + ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1); + ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true); + } else + ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 0); + + if (accountManager.addAccountExplicitly(account, serverInfo.getPassword(), userData)) { + // account created, now create calendars + boolean syncCalendars = false; + for (ServerInfo.ResourceInfo calendar : serverInfo.getCalendars()) + if (calendar.isEnabled()) + try { + LocalCalendar.create(account, getActivity().getContentResolver(), calendar); + syncCalendars = true; + } catch (LocalStorageException e) { + Toast.makeText(getActivity(), "Couldn't create calendar(s): " + e.getMessage(), Toast.LENGTH_LONG).show(); + } + if (syncCalendars) { + ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 1); + ContentResolver.setSyncAutomatically(account, CalendarContract.AUTHORITY, true); + } else + ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 0); + + getActivity().finish(); + } else + Toast.makeText(getActivity(), "Couldn't create account (account with this name already existing?)", Toast.LENGTH_LONG).show(); + } + + + // input validation + + @Override + public void onPrepareOptionsMenu(Menu menu) { + boolean ok = false; + ok = editAccountName.getText().length() > 0; + MenuItem item = menu.findItem(R.id.add_account); + item.setEnabled(ok); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + getActivity().invalidateOptionsMenu(); + } + + @Override + public void afterTextChanged(Editable s) { + } +} diff --git a/src/at/bitfire/davdroid/syncadapter/AccountSettings.java b/src/at/bitfire/davdroid/syncadapter/AccountSettings.java new file mode 100644 index 00000000..a58fe407 --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/AccountSettings.java @@ -0,0 +1,183 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import java.net.URI; +import java.net.URISyntaxException; + +import lombok.Cleanup; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.provider.CalendarContract; +import android.provider.CalendarContract.Calendars; +import android.util.Log; +import at.bitfire.davdroid.resource.ServerInfo; +import ezvcard.VCardVersion; + +public class AccountSettings { + private final static String TAG = "davdroid.AccountSettings"; + + private final static int CURRENT_VERSION = 1; + private final static String + KEY_SETTINGS_VERSION = "version", + + KEY_USERNAME = "user_name", + KEY_AUTH_PREEMPTIVE = "auth_preemptive", + + KEY_ADDRESSBOOK_URL = "addressbook_url", + KEY_ADDRESSBOOK_CTAG = "addressbook_ctag", + KEY_ADDRESSBOOK_VCARD_VERSION = "addressbook_vcard_version"; + + Context context; + AccountManager accountManager; + Account account; + + + public AccountSettings(Context context, Account account) { + this.context = context; + this.account = account; + + accountManager = AccountManager.get(context); + + synchronized(AccountSettings.class) { + int version = 0; + try { + version = Integer.parseInt(accountManager.getUserData(account, KEY_SETTINGS_VERSION)); + } catch(NumberFormatException e) { + } + if (version < CURRENT_VERSION) + update(version); + } + } + + + public static Bundle createBundle(ServerInfo serverInfo) { + Bundle bundle = new Bundle(); + bundle.putString(KEY_SETTINGS_VERSION, String.valueOf(CURRENT_VERSION)); + bundle.putString(KEY_USERNAME, serverInfo.getUserName()); + bundle.putString(KEY_AUTH_PREEMPTIVE, Boolean.toString(serverInfo.isAuthPreemptive())); + for (ServerInfo.ResourceInfo addressBook : serverInfo.getAddressBooks()) + if (addressBook.isEnabled()) { + bundle.putString(KEY_ADDRESSBOOK_URL, addressBook.getURL()); + bundle.putString(KEY_ADDRESSBOOK_VCARD_VERSION, addressBook.getVCardVersion().getVersion()); + continue; + } + return bundle; + } + + + // general settings + + public String getUserName() { + return accountManager.getUserData(account, KEY_USERNAME); + } + + public String getPassword() { + return accountManager.getPassword(account); + } + + public boolean getPreemptiveAuth() { + return Boolean.parseBoolean(accountManager.getUserData(account, KEY_AUTH_PREEMPTIVE)); + } + + + // address book (CardDAV) settings + + public String getAddressBookURL() { + return accountManager.getUserData(account, KEY_ADDRESSBOOK_URL); + } + + public String getAddressBookCTag() { + return accountManager.getUserData(account, KEY_ADDRESSBOOK_CTAG); + } + + public void setAddressBookCTag(String cTag) { + accountManager.setUserData(account, KEY_ADDRESSBOOK_CTAG, cTag); + } + + public VCardVersion getAddressBookVCardVersion() { + VCardVersion version = VCardVersion.V3_0; + String versionStr = accountManager.getUserData(account, KEY_ADDRESSBOOK_VCARD_VERSION); + if (versionStr != null) + version = VCardVersion.valueOfByStr(versionStr); + return version; + } + + + // update from previous account settings + + private void update(int fromVersion) { + Log.i(TAG, "Account settings must be updated from v" + fromVersion + " to v" + CURRENT_VERSION); + for (int toVersion = CURRENT_VERSION; toVersion > fromVersion; toVersion--) + update(fromVersion, toVersion); + } + + private void update(int fromVersion, int toVersion) { + Log.i(TAG, "Updating account settings from v" + fromVersion + " to " + toVersion); + try { + if (fromVersion == 0 && toVersion == 1) + update_0_1(); + else + Log.wtf(TAG, "Don't know how to update settings from v" + fromVersion + " to v" + toVersion); + } catch(Exception e) { + Log.e(TAG, "Couldn't update account settings (DAVdroid will probably crash)!", e); + } + } + + private void update_0_1() throws URISyntaxException { + String v0_principalURL = accountManager.getUserData(account, "principal_url"), + v0_addressBookPath = accountManager.getUserData(account, "addressbook_path"); + Log.d(TAG, "Old principal URL = " + v0_principalURL); + Log.d(TAG, "Old address book path = " + v0_addressBookPath); + + URI principalURI = new URI(v0_principalURL); + + // update address book + if (v0_addressBookPath != null) { + String addressBookURL = principalURI.resolve(v0_addressBookPath).toASCIIString(); + Log.d(TAG, "New address book URL = " + addressBookURL); + accountManager.setUserData(account, "addressbook_url", addressBookURL); + } + + // update calendars + ContentResolver resolver = context.getContentResolver(); + Uri calendars = Calendars.CONTENT_URI.buildUpon() + .appendQueryParameter(Calendars.ACCOUNT_NAME, account.name) + .appendQueryParameter(Calendars.ACCOUNT_TYPE, account.type) + .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true").build(); + @Cleanup Cursor cursor = resolver.query(calendars, new String[] { Calendars._ID, Calendars.NAME }, null, null, null); + while (cursor != null && cursor.moveToNext()) { + int id = cursor.getInt(0); + String v0_path = cursor.getString(1), + v1_url = principalURI.resolve(v0_path).toASCIIString(); + Log.d(TAG, "Updating calendar #" + id + " name: " + v0_path + " -> " + v1_url); + Uri calendar = ContentUris.appendId(Calendars.CONTENT_URI.buildUpon() + .appendQueryParameter(Calendars.ACCOUNT_NAME, account.name) + .appendQueryParameter(Calendars.ACCOUNT_TYPE, account.type) + .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true"), id).build(); + ContentValues newValues = new ContentValues(1); + newValues.put(Calendars.NAME, v1_url); + if (resolver.update(calendar, newValues, null, null) != 1) + Log.e(TAG, "Number of modified calendars != 1"); + } + + Log.d(TAG, "Cleaning old principal URL and address book path"); + accountManager.setUserData(account, "principal_url", null); + accountManager.setUserData(account, "addressbook_path", null); + + Log.d(TAG, "Updated settings successfully!"); + accountManager.setUserData(account, KEY_SETTINGS_VERSION, "1"); + } +} diff --git a/src/at/bitfire/davdroid/syncadapter/AddAccountActivity.java b/src/at/bitfire/davdroid/syncadapter/AddAccountActivity.java new file mode 100644 index 00000000..f65f42f9 --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/AddAccountActivity.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import at.bitfire.davdroid.Constants; +import at.bitfire.davdroid.R; + +public class AddAccountActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.add_account); + + if (savedInstanceState == null) { // first call + getFragmentManager().beginTransaction() + .add(R.id.fragment_container, new LoginTypeFragment(), "login_type") + .commit(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.add_account, menu); + return true; + } + + public void showHelp(MenuItem item) { + startActivityForResult(new Intent(Intent.ACTION_VIEW, Uri.parse(Constants.WEB_URL_HELP)), 0); + } + +} diff --git a/src/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.java b/src/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.java new file mode 100644 index 00000000..f00678cd --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import java.net.MalformedURLException; +import java.util.HashMap; +import java.util.Map; + +import android.accounts.Account; +import android.app.Service; +import android.content.ContentProviderClient; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import at.bitfire.davdroid.resource.CalDavCalendar; +import at.bitfire.davdroid.resource.LocalCalendar; +import at.bitfire.davdroid.resource.LocalCollection; +import at.bitfire.davdroid.resource.RemoteCollection; + +public class CalendarsSyncAdapterService extends Service { + private static SyncAdapter syncAdapter; + + + @Override + public void onCreate() { + if (syncAdapter == null) + syncAdapter = new SyncAdapter(getApplicationContext()); + } + + @Override + public void onDestroy() { + syncAdapter.close(); + syncAdapter = null; + } + + @Override + public IBinder onBind(Intent intent) { + return syncAdapter.getSyncAdapterBinder(); + } + + + private static class SyncAdapter extends DavSyncAdapter { + private final static String TAG = "davdroid.CalendarsSyncAdapter"; + + + private SyncAdapter(Context context) { + super(context); + } + + @Override + protected Map, RemoteCollection> getSyncPairs(Account account, ContentProviderClient provider) { + AccountSettings settings = new AccountSettings(getContext(), account); + String userName = settings.getUserName(), + password = settings.getPassword(); + boolean preemptive = settings.getPreemptiveAuth(); + + try { + Map, RemoteCollection> map = new HashMap, RemoteCollection>(); + + for (LocalCalendar calendar : LocalCalendar.findAll(account, provider)) { + RemoteCollection dav = new CalDavCalendar(httpClient, calendar.getUrl(), userName, password, preemptive); + map.put(calendar, dav); + } + return map; + } catch (RemoteException ex) { + Log.e(TAG, "Couldn't find local calendars", ex); + } catch (MalformedURLException ex) { + Log.e(TAG, "Couldn't build calendar URI", ex); + } + + return null; + } + } +} diff --git a/src/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.java b/src/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.java new file mode 100644 index 00000000..88328700 --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import java.net.MalformedURLException; +import java.util.HashMap; +import java.util.Map; + +import android.accounts.Account; +import android.app.Service; +import android.content.ContentProviderClient; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.util.Log; +import at.bitfire.davdroid.resource.CardDavAddressBook; +import at.bitfire.davdroid.resource.LocalAddressBook; +import at.bitfire.davdroid.resource.LocalCollection; +import at.bitfire.davdroid.resource.RemoteCollection; + +public class ContactsSyncAdapterService extends Service { + private static ContactsSyncAdapter syncAdapter; + + + @Override + public void onCreate() { + if (syncAdapter == null) + syncAdapter = new ContactsSyncAdapter(getApplicationContext()); + } + + @Override + public void onDestroy() { + syncAdapter.close(); + syncAdapter = null; + } + + @Override + public IBinder onBind(Intent intent) { + return syncAdapter.getSyncAdapterBinder(); + } + + + private static class ContactsSyncAdapter extends DavSyncAdapter { + private final static String TAG = "davdroid.ContactsSyncAdapter"; + + + private ContactsSyncAdapter(Context context) { + super(context); + } + + @Override + protected Map, RemoteCollection> getSyncPairs(Account account, ContentProviderClient provider) { + AccountSettings settings = new AccountSettings(getContext(), account); + String userName = settings.getUserName(), + password = settings.getPassword(); + boolean preemptive = settings.getPreemptiveAuth(); + + String addressBookURL = settings.getAddressBookURL(); + if (addressBookURL == null) + return null; + + try { + LocalCollection database = new LocalAddressBook(account, provider, settings); + RemoteCollection dav = new CardDavAddressBook(httpClient, addressBookURL, userName, password, preemptive); + + Map, RemoteCollection> map = new HashMap, RemoteCollection>(); + map.put(database, dav); + + return map; + } catch (MalformedURLException ex) { + Log.e(TAG, "Couldn't build address book URI", ex); + } + + return null; + } + } +} diff --git a/src/at/bitfire/davdroid/syncadapter/DavSyncAdapter.java b/src/at/bitfire/davdroid/syncadapter/DavSyncAdapter.java new file mode 100644 index 00000000..71189f4e --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/DavSyncAdapter.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import java.io.Closeable; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import lombok.Getter; + +import org.apache.http.HttpStatus; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.content.AbstractThreadedSyncAdapter; +import android.content.ContentProviderClient; +import android.content.ContentResolver; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SyncResult; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.provider.Settings; +import android.util.Log; +import at.bitfire.davdroid.Constants; +import at.bitfire.davdroid.resource.LocalCollection; +import at.bitfire.davdroid.resource.LocalStorageException; +import at.bitfire.davdroid.resource.RemoteCollection; +import at.bitfire.davdroid.webdav.DavException; +import at.bitfire.davdroid.webdav.DavHttpClient; +import at.bitfire.davdroid.webdav.HttpException; +import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient; + +public abstract class DavSyncAdapter extends AbstractThreadedSyncAdapter implements Closeable { + private final static String TAG = "davdroid.DavSyncAdapter"; + + @Getter private static String androidID; + + protected AccountManager accountManager; + + /* We use one static httpClient for + * - all sync adapters (CalendarsSyncAdapter, ContactsSyncAdapter) + * - and all threads (= accounts) of each sync adapter + * so that HttpClient's threaded pool management can do its best. + */ + protected static CloseableHttpClient httpClient; + + /* One static read/write lock pair for the static httpClient: + * Use the READ lock when httpClient will only be called (to prevent it from being unset while being used). + * Use the WRITE lock when httpClient will be modified (set/unset). */ + private final static ReentrantReadWriteLock httpClientLock = new ReentrantReadWriteLock(); + + + public DavSyncAdapter(Context context) { + super(context, true); + + synchronized(this) { + if (androidID == null) + androidID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); + } + + accountManager = AccountManager.get(context); + } + + @Override + public void close() { + Log.d(TAG, "Closing httpClient"); + + // may be called from a GUI thread, so we need an AsyncTask + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + try { + httpClientLock.writeLock().lock(); + if (httpClient != null) { + httpClient.close(); + httpClient = null; + } + httpClientLock.writeLock().unlock(); + } catch (IOException e) { + Log.w(TAG, "Couldn't close HTTP client", e); + } + return null; + } + }.execute(); + } + + protected abstract Map, RemoteCollection> getSyncPairs(Account account, ContentProviderClient provider); + + + @Override + public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { + Log.i(TAG, "Performing sync for authority " + authority); + + // set class loader for iCal4j ResourceLoader + Thread.currentThread().setContextClassLoader(getContext().getClassLoader()); + + // create httpClient, if necessary + httpClientLock.writeLock().lock(); + if (httpClient == null) { + Log.d(TAG, "Creating new DavHttpClient"); + SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getContext()); + httpClient = DavHttpClient.create( + settings.getBoolean(Constants.SETTING_DISABLE_COMPRESSION, false), + settings.getBoolean(Constants.SETTING_NETWORK_LOGGING, false) + ); + } + + // prevent httpClient shutdown until we're ready by holding a read lock + // acquiring read lock before releasing write lock will downgrade the write lock to a read lock + httpClientLock.readLock().lock(); + httpClientLock.writeLock().unlock(); + + // TODO use VCard 4.0 if possible + AccountSettings accountSettings = new AccountSettings(getContext(), account); + Log.d(TAG, "Server supports VCard version " + accountSettings.getAddressBookVCardVersion()); + + try { + // get local <-> remote collection pairs + Map, RemoteCollection> syncCollections = getSyncPairs(account, provider); + if (syncCollections == null) + Log.i(TAG, "Nothing to synchronize"); + else + try { + for (Map.Entry, RemoteCollection> entry : syncCollections.entrySet()) + new SyncManager(entry.getKey(), entry.getValue()).synchronize(extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL), syncResult); + } catch (DavException ex) { + syncResult.stats.numParseExceptions++; + Log.e(TAG, "Invalid DAV response", ex); + } catch (HttpException ex) { + if (ex.getCode() == HttpStatus.SC_UNAUTHORIZED) { + Log.e(TAG, "HTTP Unauthorized " + ex.getCode(), ex); + syncResult.stats.numAuthExceptions++; + } else if (ex.isClientError()) { + Log.e(TAG, "Hard HTTP error " + ex.getCode(), ex); + syncResult.stats.numParseExceptions++; + } else { + Log.w(TAG, "Soft HTTP error " + ex.getCode() + " (Android will try again later)", ex); + syncResult.stats.numIoExceptions++; + } + } catch (LocalStorageException ex) { + syncResult.databaseError = true; + Log.e(TAG, "Local storage (content provider) exception", ex); + } catch (IOException ex) { + syncResult.stats.numIoExceptions++; + Log.e(TAG, "I/O error (Android will try again later)", ex); + } catch (URISyntaxException ex) { + Log.e(TAG, "Invalid URI (file name) syntax", ex); + } + } finally { + // allow httpClient shutdown + httpClientLock.readLock().unlock(); + } + + Log.i(TAG, "Sync complete for " + authority); + } + +} diff --git a/src/at/bitfire/davdroid/syncadapter/GeneralSettingsActivity.java b/src/at/bitfire/davdroid/syncadapter/GeneralSettingsActivity.java new file mode 100644 index 00000000..9135b428 --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/GeneralSettingsActivity.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.preference.PreferenceFragment; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import at.bitfire.davdroid.R; + +public class GeneralSettingsActivity extends Activity { + final static String URL_REPORT_ISSUE = "https://github.com/bitfireAT/davdroid/blob/master/CONTRIBUTING.md#reporting-issues"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getFragmentManager().beginTransaction() + .replace(android.R.id.content, new GeneralSettingsFragment()) + .commit(); + } + + public void reportIssue(MenuItem item) { + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(URL_REPORT_ISSUE))); + } + + + public static class GeneralSettingsFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getPreferenceManager().setSharedPreferencesMode(Context.MODE_MULTI_PROCESS); + addPreferencesFromResource(R.xml.general_settings); + + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.debug_settings, menu); + } + } +} diff --git a/src/at/bitfire/davdroid/syncadapter/LoginEmailFragment.java b/src/at/bitfire/davdroid/syncadapter/LoginEmailFragment.java new file mode 100644 index 00000000..e7d2e3ec --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/LoginEmailFragment.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import java.net.URI; +import java.net.URISyntaxException; + +import android.app.DialogFragment; +import android.app.Fragment; +import android.app.FragmentTransaction; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import at.bitfire.davdroid.R; + +public class LoginEmailFragment extends Fragment implements TextWatcher { + + protected EditText editEmail, editPassword; + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.login_email, container, false); + + editEmail = (EditText)v.findViewById(R.id.email_address); + editEmail.addTextChangedListener(this); + editPassword = (EditText)v.findViewById(R.id.password); + editPassword.addTextChangedListener(this); + + setHasOptionsMenu(true); + return v; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.only_next, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.next: + FragmentTransaction ft = getFragmentManager().beginTransaction(); + + Bundle args = new Bundle(); + String email = editEmail.getText().toString(); + args.putString(QueryServerDialogFragment.EXTRA_BASE_URI, "mailto:" + email); + args.putString(QueryServerDialogFragment.EXTRA_USER_NAME, email); + args.putString(QueryServerDialogFragment.EXTRA_PASSWORD, editPassword.getText().toString()); + args.putBoolean(QueryServerDialogFragment.EXTRA_AUTH_PREEMPTIVE, true); + + DialogFragment dialog = new QueryServerDialogFragment(); + dialog.setArguments(args); + dialog.show(ft, QueryServerDialogFragment.class.getName()); + break; + default: + return false; + } + return true; + } + + + // input validation + + @Override + public void onPrepareOptionsMenu(Menu menu) { + boolean passwordOk = editPassword.getText().length() > 0, + emailOk = false; + + String email = editEmail.getText().toString(); + try { + URI uri = new URI("mailto:" + email); + if (uri.isOpaque()) { + int pos = email.lastIndexOf("@"); + if (pos != -1) + emailOk = !email.substring(pos+1).isEmpty(); + } + } catch (URISyntaxException e) { + // invalid mailto: URI + } + + MenuItem item = menu.findItem(R.id.next); + item.setEnabled(emailOk && passwordOk); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + getActivity().invalidateOptionsMenu(); + } + + @Override + public void afterTextChanged(Editable s) { + } + +} diff --git a/src/at/bitfire/davdroid/syncadapter/LoginTypeFragment.java b/src/at/bitfire/davdroid/syncadapter/LoginTypeFragment.java new file mode 100644 index 00000000..8507bcc6 --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/LoginTypeFragment.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.RadioButton; +import at.bitfire.davdroid.R; + +public class LoginTypeFragment extends Fragment { + + protected RadioButton btnTypeEmail, btnTypeURL; + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.login_type, container, false); + + btnTypeEmail = (RadioButton)v.findViewById(R.id.login_type_email); + btnTypeURL = (RadioButton)v.findViewById(R.id.login_type_url); + + setHasOptionsMenu(true); + + return v; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.only_next, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.next: + Fragment loginFragment = btnTypeEmail.isChecked() ? new LoginEmailFragment() : new LoginURLFragment(); + getFragmentManager().beginTransaction() + .replace(R.id.fragment_container, loginFragment) + .addToBackStack(null) + .commitAllowingStateLoss(); + return true; + default: + return false; + } + } +} diff --git a/src/at/bitfire/davdroid/syncadapter/LoginURLFragment.java b/src/at/bitfire/davdroid/syncadapter/LoginURLFragment.java new file mode 100644 index 00000000..d504ff56 --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/LoginURLFragment.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import java.net.URI; +import java.net.URISyntaxException; + +import org.apache.commons.lang.StringUtils; + +import android.app.DialogFragment; +import android.app.Fragment; +import android.app.FragmentTransaction; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.TextView; +import at.bitfire.davdroid.R; +import at.bitfire.davdroid.URLUtils; + +public class LoginURLFragment extends Fragment implements TextWatcher { + protected String scheme; + + protected TextView textHttpWarning; + protected EditText editBaseURI, editUserName, editPassword; + protected CheckBox checkboxPreemptive; + protected Button btnNext; + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.login_url, container, false); + + // protocol selection spinner + textHttpWarning = (TextView) v.findViewById(R.id.http_warning); + + Spinner spnrScheme = (Spinner) v.findViewById(R.id.login_scheme); + spnrScheme.setOnItemSelectedListener(new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + scheme = parent.getAdapter().getItem(position).toString(); + textHttpWarning.setVisibility(scheme.equals("https://") ? View.GONE : View.VISIBLE); + } + + @Override + public void onNothingSelected(AdapterView parent) { + scheme = null; + } + }); + spnrScheme.setSelection(1); // HTTPS + + // other input fields + editBaseURI = (EditText) v.findViewById(R.id.login_host_path); + editBaseURI.addTextChangedListener(this); + + editUserName = (EditText) v.findViewById(R.id.userName); + editUserName.addTextChangedListener(this); + + editPassword = (EditText) v.findViewById(R.id.password); + editPassword.addTextChangedListener(this); + + checkboxPreemptive = (CheckBox) v.findViewById(R.id.auth_preemptive); + + // hook into action bar + setHasOptionsMenu(true); + + return v; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.only_next, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.next: + FragmentTransaction ft = getFragmentManager().beginTransaction(); + + Bundle args = new Bundle(); + String host_path = editBaseURI.getText().toString(); + args.putString(QueryServerDialogFragment.EXTRA_BASE_URI, URLUtils.sanitize(scheme + host_path)); + args.putString(QueryServerDialogFragment.EXTRA_USER_NAME, editUserName.getText().toString()); + args.putString(QueryServerDialogFragment.EXTRA_PASSWORD, editPassword.getText().toString()); + args.putBoolean(QueryServerDialogFragment.EXTRA_AUTH_PREEMPTIVE, checkboxPreemptive.isChecked()); + + DialogFragment dialog = new QueryServerDialogFragment(); + dialog.setArguments(args); + dialog.show(ft, QueryServerDialogFragment.class.getName()); + break; + default: + return false; + } + return true; + } + + + // input validation + + @Override + public void onPrepareOptionsMenu(Menu menu) { + boolean ok = + editUserName.getText().length() > 0 && + editPassword.getText().length() > 0; + + if (ok) + // check host name + try { + URI uri = new URI(URLUtils.sanitize(scheme + editBaseURI.getText().toString())); + if (StringUtils.isBlank(uri.getHost())) + ok = false; + } catch (URISyntaxException e) { + ok = false; + } + + MenuItem item = menu.findItem(R.id.next); + item.setEnabled(ok); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + getActivity().invalidateOptionsMenu(); + } + + @Override + public void afterTextChanged(Editable s) { + } +} diff --git a/src/at/bitfire/davdroid/syncadapter/QueryServerDialogFragment.java b/src/at/bitfire/davdroid/syncadapter/QueryServerDialogFragment.java new file mode 100644 index 00000000..2596cfe0 --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/QueryServerDialogFragment.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import lombok.Cleanup; +import android.app.DialogFragment; +import android.app.LoaderManager.LoaderCallbacks; +import android.content.AsyncTaskLoader; +import android.content.Context; +import android.content.Loader; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ProgressBar; +import android.widget.Toast; +import at.bitfire.davdroid.R; +import at.bitfire.davdroid.resource.DavResourceFinder; +import at.bitfire.davdroid.resource.ServerInfo; +import at.bitfire.davdroid.webdav.DavException; +import ch.boye.httpclientandroidlib.HttpException; + +public class QueryServerDialogFragment extends DialogFragment implements LoaderCallbacks { + private static final String TAG = "davdroid.QueryServerDialogFragment"; + public static final String + EXTRA_BASE_URI = "base_uri", + EXTRA_USER_NAME = "user_name", + EXTRA_PASSWORD = "password", + EXTRA_AUTH_PREEMPTIVE = "auth_preemptive"; + + ProgressBar progressBar; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setStyle(DialogFragment.STYLE_NO_TITLE, android.R.style.Theme_Holo_Light_Dialog); + setCancelable(false); + + Loader loader = getLoaderManager().initLoader(0, getArguments(), this); + if (savedInstanceState == null) // http://code.google.com/p/android/issues/detail?id=14944 + loader.forceLoad(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.query_server, container, false); + return v; + } + + @Override + public Loader onCreateLoader(int id, Bundle args) { + Log.i(TAG, "onCreateLoader"); + return new ServerInfoLoader(getActivity(), args); + } + + @Override + public void onLoadFinished(Loader loader, ServerInfo serverInfo) { + if (serverInfo.getErrorMessage() != null) + Toast.makeText(getActivity(), serverInfo.getErrorMessage(), Toast.LENGTH_LONG).show(); + else { + SelectCollectionsFragment selectCollections = new SelectCollectionsFragment(); + Bundle arguments = new Bundle(); + arguments.putSerializable(SelectCollectionsFragment.KEY_SERVER_INFO, serverInfo); + selectCollections.setArguments(arguments); + + getFragmentManager().beginTransaction() + .replace(R.id.fragment_container, selectCollections) + .addToBackStack(null) + .commitAllowingStateLoss(); + } + + getDialog().dismiss(); + } + + @Override + public void onLoaderReset(Loader arg0) { + } + + + static class ServerInfoLoader extends AsyncTaskLoader { + private static final String TAG = "davdroid.ServerInfoLoader"; + final Bundle args; + final Context context; + + public ServerInfoLoader(Context context, Bundle args) { + super(context); + this.context = context; + this.args = args; + } + + @Override + public ServerInfo loadInBackground() { + ServerInfo serverInfo = new ServerInfo( + URI.create(args.getString(EXTRA_BASE_URI)), + args.getString(EXTRA_USER_NAME), + args.getString(EXTRA_PASSWORD), + args.getBoolean(EXTRA_AUTH_PREEMPTIVE) + ); + + try { + @Cleanup DavResourceFinder finder = new DavResourceFinder(context); + finder.findResources(serverInfo); + } catch (URISyntaxException e) { + serverInfo.setErrorMessage(getContext().getString(R.string.exception_uri_syntax, e.getMessage())); + } catch (IOException e) { + serverInfo.setErrorMessage(getContext().getString(R.string.exception_io, e.getLocalizedMessage())); + } catch (HttpException e) { + Log.e(TAG, "HTTP error while querying server info", e); + serverInfo.setErrorMessage(getContext().getString(R.string.exception_http, e.getLocalizedMessage())); + } catch (DavException e) { + Log.e(TAG, "DAV error while querying server info", e); + serverInfo.setErrorMessage(getContext().getString(R.string.exception_incapable_resource, e.getLocalizedMessage())); + } + + return serverInfo; + } + + } +} diff --git a/src/at/bitfire/davdroid/syncadapter/SelectCollectionsAdapter.java b/src/at/bitfire/davdroid/syncadapter/SelectCollectionsAdapter.java new file mode 100644 index 00000000..95c871ed --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/SelectCollectionsAdapter.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import lombok.Getter; +import android.annotation.SuppressLint; +import android.content.Context; +import android.text.Html; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.CheckedTextView; +import android.widget.ListAdapter; +import at.bitfire.davdroid.R; +import at.bitfire.davdroid.resource.ServerInfo; +import at.bitfire.davdroid.resource.ServerInfo.ResourceInfo.Type; + +public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter { + final static int TYPE_ADDRESS_BOOKS_HEADING = 0, + TYPE_ADDRESS_BOOKS_ROW = 1, + TYPE_CALENDARS_HEADING = 2, + TYPE_CALENDARS_ROW = 3; + + protected Context context; + protected ServerInfo serverInfo; + @Getter protected int nAddressBooks, nCalendars; + + + public SelectCollectionsAdapter(Context context, ServerInfo serverInfo) { + this.context = context; + + this.serverInfo = serverInfo; + nAddressBooks = (serverInfo.getAddressBooks() == null) ? 0 : serverInfo.getAddressBooks().size(); + nCalendars = (serverInfo.getCalendars() == null) ? 0 : serverInfo.getCalendars().size(); + } + + + // item data + + @Override + public int getCount() { + return nAddressBooks + nCalendars + 2; + } + + @Override + public Object getItem(int position) { + if (position > 0 && position <= nAddressBooks) + return serverInfo.getAddressBooks().get(position - 1); + else if (position > nAddressBooks + 1) + return serverInfo.getCalendars().get(position - nAddressBooks - 2); + return null; + } + + @Override + public boolean hasStableIds() { + return true; + } + + @Override + public long getItemId(int position) { + return position; + } + + + // item views + + @Override + public int getViewTypeCount() { + return 4; + } + + @Override + public int getItemViewType(int position) { + if (position == 0) + return TYPE_ADDRESS_BOOKS_HEADING; + else if (position <= nAddressBooks) + return TYPE_ADDRESS_BOOKS_ROW; + else if (position == nAddressBooks + 1) + return TYPE_CALENDARS_HEADING; + else if (position <= nAddressBooks + nCalendars + 1) + return TYPE_CALENDARS_ROW; + else + return IGNORE_ITEM_VIEW_TYPE; + } + + @Override + @SuppressLint("InflateParams") + public View getView(int position, View convertView, ViewGroup parent) { + View v = convertView; + + // step 1: get view (either by creating or recycling) + if (v == null) { + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + switch (getItemViewType(position)) { + case TYPE_ADDRESS_BOOKS_HEADING: + v = inflater.inflate(R.layout.address_books_heading, parent, false); + break; + case TYPE_ADDRESS_BOOKS_ROW: + v = inflater.inflate(android.R.layout.simple_list_item_single_choice, null); + v.setPadding(0, 8, 0, 8); + break; + case TYPE_CALENDARS_HEADING: + v = inflater.inflate(R.layout.calendars_heading, parent, false); + break; + case TYPE_CALENDARS_ROW: + v = inflater.inflate(android.R.layout.simple_list_item_multiple_choice, null); + v.setPadding(0, 8, 0, 8); + } + } + + // step 2: fill view with content + switch (getItemViewType(position)) { + case TYPE_ADDRESS_BOOKS_ROW: + setContent((CheckedTextView)v, R.drawable.addressbook, (ServerInfo.ResourceInfo)getItem(position)); + break; + case TYPE_CALENDARS_ROW: + setContent((CheckedTextView)v, R.drawable.calendar, (ServerInfo.ResourceInfo)getItem(position)); + } + + return v; + } + + protected void setContent(CheckedTextView view, int collectionIcon, ServerInfo.ResourceInfo info) { + // set layout and icons + view.setCompoundDrawablesWithIntrinsicBounds(collectionIcon, 0, info.isReadOnly() ? R.drawable.ic_read_only : 0, 0); + view.setCompoundDrawablePadding(10); + + // set text + String title = info.getTitle(); + if (title == null) // unnamed collection + title = context.getString((info.getType() == Type.ADDRESS_BOOK) ? + R.string.setup_address_book : R.string.setup_calendar); + title = "" + title + ""; + if (info.isReadOnly()) + title = title + " (" + context.getString(R.string.setup_read_only) + ")"; + + String description = info.getDescription(); + if (description == null) + description = info.getURL(); + + // FIXME escape HTML + view.setText(Html.fromHtml(title + "
" + description)); + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int position) { + int type = getItemViewType(position); + return (type == TYPE_ADDRESS_BOOKS_ROW || type == TYPE_CALENDARS_ROW); + } +} diff --git a/src/at/bitfire/davdroid/syncadapter/SelectCollectionsFragment.java b/src/at/bitfire/davdroid/syncadapter/SelectCollectionsFragment.java new file mode 100644 index 00000000..c7a044b2 --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/SelectCollectionsFragment.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import android.app.ListFragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ListAdapter; +import android.widget.ListView; +import at.bitfire.davdroid.R; +import at.bitfire.davdroid.resource.ServerInfo; + +public class SelectCollectionsFragment extends ListFragment { + public static final String KEY_SERVER_INFO = "server_info"; + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = super.onCreateView(inflater, container, savedInstanceState); + setHasOptionsMenu(true); + return v; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + setListAdapter(null); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + final ListView listView = getListView(); + listView.setPadding(20, 30, 20, 30); + + View header = getActivity().getLayoutInflater().inflate(R.layout.select_collections_header, getListView(), false); + listView.addHeaderView(header, getListView(), false); + + final ServerInfo serverInfo = (ServerInfo)getArguments().getSerializable(KEY_SERVER_INFO); + final SelectCollectionsAdapter adapter = new SelectCollectionsAdapter(view.getContext(), serverInfo); + setListAdapter(adapter); + + listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + listView.setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + int itemPosition = position - 1; // one list header view at pos. 0 + if (adapter.getItemViewType(itemPosition) == SelectCollectionsAdapter.TYPE_ADDRESS_BOOKS_ROW) { + // unselect all other address books + for (int pos = 1; pos <= adapter.getNAddressBooks(); pos++) + if (pos != itemPosition) + listView.setItemChecked(pos + 1, false); + } + + getActivity().invalidateOptionsMenu(); + } + }); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.only_next, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.next: + ServerInfo serverInfo = (ServerInfo)getArguments().getSerializable(KEY_SERVER_INFO); + + // synchronize only selected collections + for (ServerInfo.ResourceInfo addressBook : serverInfo.getAddressBooks()) + addressBook.setEnabled(false); + for (ServerInfo.ResourceInfo calendar : serverInfo.getCalendars()) + calendar.setEnabled(false); + + ListAdapter adapter = getListView().getAdapter(); + for (long id : getListView().getCheckedItemIds()) { + int position = (int)id + 1; // +1 because header view is inserted at pos. 0 + ServerInfo.ResourceInfo info = (ServerInfo.ResourceInfo)adapter.getItem(position); + info.setEnabled(true); + } + + // pass to "account details" fragment + AccountDetailsFragment accountDetails = new AccountDetailsFragment(); + Bundle arguments = new Bundle(); + arguments.putSerializable(SelectCollectionsFragment.KEY_SERVER_INFO, serverInfo); + accountDetails.setArguments(arguments); + + getFragmentManager().beginTransaction() + .replace(R.id.fragment_container, accountDetails) + .addToBackStack(null) + .commitAllowingStateLoss(); + break; + default: + return false; + } + return true; + } + + + // input validation + + @Override + public void onPrepareOptionsMenu(Menu menu) { + boolean ok = false; + try { + ok = getListView().getCheckedItemCount() > 0; + } catch(IllegalStateException e) { + } + MenuItem item = menu.findItem(R.id.next); + item.setEnabled(ok); + } +} diff --git a/src/at/bitfire/davdroid/syncadapter/SyncManager.java b/src/at/bitfire/davdroid/syncadapter/SyncManager.java new file mode 100644 index 00000000..c1bf2fa2 --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/SyncManager.java @@ -0,0 +1,214 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.HashSet; +import java.util.Set; + +import net.fortuna.ical4j.model.ValidationException; +import android.content.SyncResult; +import android.util.Log; +import at.bitfire.davdroid.ArrayUtils; +import at.bitfire.davdroid.resource.LocalCollection; +import at.bitfire.davdroid.resource.LocalStorageException; +import at.bitfire.davdroid.resource.RecordNotFoundException; +import at.bitfire.davdroid.resource.RemoteCollection; +import at.bitfire.davdroid.resource.Resource; +import at.bitfire.davdroid.webdav.DavException; +import at.bitfire.davdroid.webdav.HttpException; +import at.bitfire.davdroid.webdav.NotFoundException; +import at.bitfire.davdroid.webdav.PreconditionFailedException; + +public class SyncManager { + private static final String TAG = "davdroid.SyncManager"; + + private static final int MAX_MULTIGET_RESOURCES = 35; + + protected LocalCollection local; + protected RemoteCollection remote; + + + public SyncManager(LocalCollection local, RemoteCollection remote) { + this.local = local; + this.remote = remote; + } + + + public void synchronize(boolean manualSync, SyncResult syncResult) throws URISyntaxException, LocalStorageException, IOException, HttpException, DavException { + // PHASE 1: push local changes to server + int deletedRemotely = pushDeleted(), + addedRemotely = pushNew(), + updatedRemotely = pushDirty(); + + syncResult.stats.numEntries = deletedRemotely + addedRemotely + updatedRemotely; + + // PHASE 2A: check if there's a reason to do a sync with remote (= forced sync or remote CTag changed) + boolean fetchCollection = syncResult.stats.numEntries > 0; + if (manualSync) { + Log.i(TAG, "Synchronization forced"); + fetchCollection = true; + } + if (!fetchCollection) { + String currentCTag = remote.getCTag(), + lastCTag = local.getCTag(); + Log.d(TAG, "Last local CTag = " + lastCTag + "; current remote CTag = " + currentCTag); + if (currentCTag == null || !currentCTag.equals(lastCTag)) + fetchCollection = true; + } + + if (!fetchCollection) { + Log.i(TAG, "No local changes and CTags match, no need to sync"); + return; + } + + // PHASE 2B: detect details of remote changes + Log.i(TAG, "Fetching remote resource list"); + Set remotelyAdded = new HashSet(), + remotelyUpdated = new HashSet(); + + Resource[] remoteResources = remote.getMemberETags(); + for (Resource remoteResource : remoteResources) { + try { + Resource localResource = local.findByRemoteName(remoteResource.getName(), false); + if (localResource.getETag() == null || !localResource.getETag().equals(remoteResource.getETag())) + remotelyUpdated.add(remoteResource); + } catch(RecordNotFoundException e) { + remotelyAdded.add(remoteResource); + } + } + + // PHASE 3: pull remote changes from server + syncResult.stats.numInserts = pullNew(remotelyAdded.toArray(new Resource[0])); + syncResult.stats.numUpdates = pullChanged(remotelyUpdated.toArray(new Resource[0])); + syncResult.stats.numEntries += syncResult.stats.numInserts + syncResult.stats.numUpdates; + + Log.i(TAG, "Removing non-dirty resources that are not present remotely anymore"); + local.deleteAllExceptRemoteNames(remoteResources); + local.commit(); + + // update collection CTag + Log.i(TAG, "Sync complete, fetching new CTag"); + local.setCTag(remote.getCTag()); + } + + + private int pushDeleted() throws URISyntaxException, LocalStorageException, IOException, HttpException { + int count = 0; + long[] deletedIDs = local.findDeleted(); + + try { + Log.i(TAG, "Remotely removing " + deletedIDs.length + " deleted resource(s) (if not changed)"); + for (long id : deletedIDs) + try { + Resource res = local.findById(id, false); + if (res.getName() != null) // is this resource even present remotely? + try { + remote.delete(res); + } catch(NotFoundException e) { + Log.i(TAG, "Locally-deleted resource has already been removed from server"); + } catch(PreconditionFailedException e) { + Log.i(TAG, "Locally-deleted resource has been changed on the server in the meanwhile"); + } + + // always delete locally so that the record with the DELETED flag doesn't cause another deletion attempt + local.delete(res); + + count++; + } catch (RecordNotFoundException e) { + Log.wtf(TAG, "Couldn't read locally-deleted record", e); + } + } finally { + local.commit(); + } + return count; + } + + private int pushNew() throws URISyntaxException, LocalStorageException, IOException, HttpException { + int count = 0; + long[] newIDs = local.findNew(); + Log.i(TAG, "Uploading " + newIDs.length + " new resource(s) (if not existing)"); + try { + for (long id : newIDs) + try { + Resource res = local.findById(id, true); + String eTag = remote.add(res); + if (eTag != null) + local.updateETag(res, eTag); + local.clearDirty(res); + count++; + } catch(PreconditionFailedException e) { + Log.i(TAG, "Didn't overwrite existing resource with other content"); + } catch (ValidationException e) { + Log.e(TAG, "Couldn't create entity for adding: " + e.toString()); + } catch (RecordNotFoundException e) { + Log.wtf(TAG, "Couldn't read new record", e); + } + } finally { + local.commit(); + } + return count; + } + + private int pushDirty() throws URISyntaxException, LocalStorageException, IOException, HttpException { + int count = 0; + long[] dirtyIDs = local.findUpdated(); + Log.i(TAG, "Uploading " + dirtyIDs.length + " modified resource(s) (if not changed)"); + try { + for (long id : dirtyIDs) { + try { + Resource res = local.findById(id, true); + String eTag = remote.update(res); + if (eTag != null) + local.updateETag(res, eTag); + local.clearDirty(res); + count++; + } catch(PreconditionFailedException e) { + Log.i(TAG, "Locally changed resource has been changed on the server in the meanwhile"); + } catch (ValidationException e) { + Log.e(TAG, "Couldn't create entity for updating: " + e.toString()); + } catch (RecordNotFoundException e) { + Log.e(TAG, "Couldn't read dirty record", e); + } + } + } finally { + local.commit(); + } + return count; + } + + private int pullNew(Resource[] resourcesToAdd) throws URISyntaxException, LocalStorageException, IOException, HttpException, DavException { + int count = 0; + Log.i(TAG, "Fetching " + resourcesToAdd.length + " new remote resource(s)"); + + for (Resource[] resources : ArrayUtils.partition(resourcesToAdd, MAX_MULTIGET_RESOURCES)) + for (Resource res : remote.multiGet(resources)) { + Log.d(TAG, "Adding " + res.getName()); + local.add(res); + local.commit(); + count++; + } + return count; + } + + private int pullChanged(Resource[] resourcesToUpdate) throws URISyntaxException, LocalStorageException, IOException, HttpException, DavException { + int count = 0; + Log.i(TAG, "Fetching " + resourcesToUpdate.length + " updated remote resource(s)"); + + for (Resource[] resources : ArrayUtils.partition(resourcesToUpdate, MAX_MULTIGET_RESOURCES)) + for (Resource res : remote.multiGet(resources)) { + Log.i(TAG, "Updating " + res.getName()); + local.updateByRemoteName(res); + local.commit(); + count++; + } + return count; + } + +} diff --git a/src/at/bitfire/davdroid/syncadapter/WebDavResourceAdapter.java b/src/at/bitfire/davdroid/syncadapter/WebDavResourceAdapter.java new file mode 100644 index 00000000..9263874d --- /dev/null +++ b/src/at/bitfire/davdroid/syncadapter/WebDavResourceAdapter.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.syncadapter; + +import java.util.List; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; +import at.bitfire.davdroid.webdav.WebDavResource; + +public class WebDavResourceAdapter extends BaseAdapter { + protected int viewId; + protected LayoutInflater inflater; + WebDavResource[] items; + + public WebDavResourceAdapter(Context context, int textViewResourceId, List objects) { + viewId = textViewResourceId; + inflater = LayoutInflater.from(context); + items = objects.toArray(new WebDavResource[0]); + } + + @Override + public View getView(int position, View view, ViewGroup parent) { + WebDavResource item = items[position]; + View itemView = (View)inflater.inflate(viewId, null); + + TextView textName = (TextView) itemView.findViewById(android.R.id.text1); + textName.setText(item.getDisplayName()); + + TextView textDescription = (TextView) itemView.findViewById(android.R.id.text2); + String description = item.getDescription(); + if (description == null) + description = item.getLocation().getPath(); + textDescription.setText(description); + + return itemView; + } + + @Override + public int getCount() { + return items.length; + } + + @Override + public Object getItem(int position) { + return items[position]; + } + + @Override + public long getItemId(int position) { + return position; + } +} diff --git a/src/at/bitfire/davdroid/webdav/DavAddressbookMultiget.java b/src/at/bitfire/davdroid/webdav/DavAddressbookMultiget.java new file mode 100644 index 00000000..9b32960f --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavAddressbookMultiget.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import org.simpleframework.xml.Namespace; +import org.simpleframework.xml.NamespaceList; +import org.simpleframework.xml.Root; + +@Root(name="addressbook-multiget") +@NamespaceList({ + @Namespace(reference="DAV:"), + @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav") +}) +@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav") +public class DavAddressbookMultiget extends DavMultiget { +} diff --git a/src/at/bitfire/davdroid/webdav/DavCalendarMultiget.java b/src/at/bitfire/davdroid/webdav/DavCalendarMultiget.java new file mode 100644 index 00000000..75a7ce71 --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavCalendarMultiget.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import org.simpleframework.xml.Namespace; +import org.simpleframework.xml.NamespaceList; +import org.simpleframework.xml.Root; + +@Root(name="calendar-multiget") +@NamespaceList({ + @Namespace(reference="DAV:"), + @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") +}) +@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") +public class DavCalendarMultiget extends DavMultiget { +} diff --git a/src/at/bitfire/davdroid/webdav/DavException.java b/src/at/bitfire/davdroid/webdav/DavException.java new file mode 100644 index 00000000..506af6da --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavException.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +public class DavException extends Exception { + private static final long serialVersionUID = -2118919144443165706L; + + final private static String prefix = "Invalid DAV response: "; + + /* used to indiciate DAV protocol errors */ + + + public DavException(String message) { + super(prefix + message); + } + + public DavException(String message, Throwable ex) { + super(prefix + message, ex); + } +} diff --git a/src/at/bitfire/davdroid/webdav/DavHref.java b/src/at/bitfire/davdroid/webdav/DavHref.java new file mode 100644 index 00000000..438c4e49 --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavHref.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import org.simpleframework.xml.Namespace; +import org.simpleframework.xml.Root; +import org.simpleframework.xml.Text; + +@Root(name="href") +@Namespace(prefix="D",reference="DAV:") +public class DavHref { + @Text + String href; + + DavHref() { + } + + public DavHref(String href) { + this.href = href; + } +} diff --git a/src/at/bitfire/davdroid/webdav/DavHttpClient.java b/src/at/bitfire/davdroid/webdav/DavHttpClient.java new file mode 100644 index 00000000..b4d1ee16 --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavHttpClient.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import android.util.Log; +import at.bitfire.davdroid.Constants; +import ch.boye.httpclientandroidlib.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.config.Registry; +import ch.boye.httpclientandroidlib.config.RegistryBuilder; +import ch.boye.httpclientandroidlib.conn.socket.ConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.socket.PlainConnectionSocketFactory; +import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient; +import ch.boye.httpclientandroidlib.impl.client.HttpClientBuilder; +import ch.boye.httpclientandroidlib.impl.client.HttpClients; +import ch.boye.httpclientandroidlib.impl.conn.ManagedHttpClientConnectionFactory; +import ch.boye.httpclientandroidlib.impl.conn.PoolingHttpClientConnectionManager; + +public class DavHttpClient { + private final static String TAG = "davdroid.DavHttpClient"; + + private final static RequestConfig defaultRqConfig; + private final static Registry socketFactoryRegistry; + + static { + socketFactoryRegistry = RegistryBuilder. create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", TlsSniSocketFactory.INSTANCE) + .build(); + + // use request defaults from AndroidHttpClient + defaultRqConfig = RequestConfig.copy(RequestConfig.DEFAULT) + .setConnectTimeout(20*1000) + .setSocketTimeout(45*1000) + .setStaleConnectionCheckEnabled(false) + .build(); + } + + + public static CloseableHttpClient create(boolean disableCompression, boolean logTraffic) { + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); + // limits per DavHttpClient (= per DavSyncAdapter extends AbstractThreadedSyncAdapter) + connectionManager.setMaxTotal(3); // max. 3 connections in total + connectionManager.setDefaultMaxPerRoute(2); // max. 2 connections per host + + HttpClientBuilder builder = HttpClients.custom() + .useSystemProperties() + .setConnectionManager(connectionManager) + .setDefaultRequestConfig(defaultRqConfig) + .setRetryHandler(DavHttpRequestRetryHandler.INSTANCE) + .setRedirectStrategy(DavRedirectStrategy.INSTANCE) + .setUserAgent("DAVdroid/" + Constants.APP_VERSION) + .disableCookieManagement(); + + if (disableCompression) { + Log.d(TAG, "Disabling compression for debugging purposes"); + builder = builder.disableContentCompression(); + } + + if (logTraffic) + Log.d(TAG, "Logging network traffic for debugging purposes"); + ManagedHttpClientConnectionFactory.INSTANCE.wirelog.enableDebug(logTraffic); + ManagedHttpClientConnectionFactory.INSTANCE.log.enableDebug(logTraffic); + + return builder.build(); + } + +} diff --git a/src/at/bitfire/davdroid/webdav/DavHttpRequestRetryHandler.java b/src/at/bitfire/davdroid/webdav/DavHttpRequestRetryHandler.java new file mode 100644 index 00000000..6ab2a509 --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavHttpRequestRetryHandler.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import java.util.Locale; + +import org.apache.commons.lang.ArrayUtils; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.impl.client.DefaultHttpRequestRetryHandler; + +public class DavHttpRequestRetryHandler extends DefaultHttpRequestRetryHandler { + final static DavHttpRequestRetryHandler INSTANCE = new DavHttpRequestRetryHandler(); + + // see http://www.iana.org/assignments/http-methods/http-methods.xhtml + private final static String idempotentMethods[] = { + "DELETE", "GET", "HEAD", "MKCALENDAR", "MKCOL", "OPTIONS", "PROPFIND", "PROPPATCH", + "PUT", "REPORT", "SEARCH", "TRACE" + }; + + public DavHttpRequestRetryHandler() { + super(/* retry count */ 3, /* retry already sent requests? */ false); + } + + @Override + protected boolean handleAsIdempotent(final HttpRequest request) { + final String method = request.getRequestLine().getMethod().toUpperCase(Locale.ROOT); + return ArrayUtils.contains(idempotentMethods, method); + } +} diff --git a/src/at/bitfire/davdroid/webdav/DavIncapableException.java b/src/at/bitfire/davdroid/webdav/DavIncapableException.java new file mode 100644 index 00000000..794865ec --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavIncapableException.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +public class DavIncapableException extends DavException { + private static final long serialVersionUID = -7199786680939975667L; + + /* used to indicate that the server doesn't support DAV */ + + public DavIncapableException(String msg) { + super(msg); + } +} diff --git a/src/at/bitfire/davdroid/webdav/DavMultiget.java b/src/at/bitfire/davdroid/webdav/DavMultiget.java new file mode 100644 index 00000000..64162b19 --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavMultiget.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import java.util.ArrayList; +import java.util.List; + +import org.simpleframework.xml.Element; +import org.simpleframework.xml.ElementList; +import org.simpleframework.xml.Order; + +@Order(elements={"prop","href"}) +public class DavMultiget { + public enum Type { + ADDRESS_BOOK, + CALENDAR + } + + @Element + DavProp prop; + + @ElementList(inline=true) + List hrefs; + + + public static DavMultiget newRequest(Type type, String names[]) { + DavMultiget multiget = (type == Type.ADDRESS_BOOK) ? new DavAddressbookMultiget() : new DavCalendarMultiget(); + + multiget.prop = new DavProp(); + multiget.prop.getetag = new DavProp.GetETag(); + + if (type == Type.ADDRESS_BOOK) + multiget.prop.addressData = new DavProp.AddressData(); + else if (type == Type.CALENDAR) + multiget.prop.calendarData = new DavProp.CalendarData(); + + multiget.hrefs = new ArrayList(names.length); + for (String name : names) + multiget.hrefs.add(new DavHref(name)); + + return multiget; + } +} diff --git a/src/at/bitfire/davdroid/webdav/DavMultistatus.java b/src/at/bitfire/davdroid/webdav/DavMultistatus.java new file mode 100644 index 00000000..dce27c2b --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavMultistatus.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import java.util.List; + +import org.simpleframework.xml.ElementList; +import org.simpleframework.xml.Namespace; +import org.simpleframework.xml.Root; + +@Namespace(reference="DAV:") +@Root(strict=false) +public class DavMultistatus { + @ElementList(inline=true,entry="response",required=false) + List response; +} diff --git a/src/at/bitfire/davdroid/webdav/DavNoContentException.java b/src/at/bitfire/davdroid/webdav/DavNoContentException.java new file mode 100644 index 00000000..6e3464b9 --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavNoContentException.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +public class DavNoContentException extends DavException { + private static final long serialVersionUID = 6256645020350945477L; + + private final static String message = "HTTP response entity (content) expected but not received"; + + public DavNoContentException() { + super(message); + } +} diff --git a/src/at/bitfire/davdroid/webdav/DavNoMultiStatusException.java b/src/at/bitfire/davdroid/webdav/DavNoMultiStatusException.java new file mode 100644 index 00000000..07eb6e18 --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavNoMultiStatusException.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +public class DavNoMultiStatusException extends DavException { + private static final long serialVersionUID = -3600405724694229828L; + + private final static String message = "207 Multi-Status expected but not received"; + + public DavNoMultiStatusException() { + super(message); + } +} diff --git a/src/at/bitfire/davdroid/webdav/DavProp.java b/src/at/bitfire/davdroid/webdav/DavProp.java new file mode 100644 index 00000000..93f25645 --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavProp.java @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import java.util.List; + +import lombok.Getter; + +import org.simpleframework.xml.Attribute; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.ElementList; +import org.simpleframework.xml.Namespace; +import org.simpleframework.xml.Root; +import org.simpleframework.xml.Text; + +@Namespace(prefix="D",reference="DAV:") +@Root(strict=false) +public class DavProp { + + /* RFC 4918 WebDAV */ + + @Element(required=false) + ResourceType resourcetype; + + @Element(required=false) + DisplayName displayname; + + @Element(required=false) + GetCTag getctag; + + @Element(required=false) + GetETag getetag; + + @Root(strict=false) + public static class ResourceType { + @Element(required=false) + @Getter private Collection collection; + public static class Collection { } + + @Element(required=false) + @Getter private Addressbook addressbook; + @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav") + public static class Addressbook { } + + @Element(required=false) + @Getter private Calendar calendar; + @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") + public static class Calendar { } + } + + public static class DisplayName { + @Text(required=false) + @Getter private String displayName; + } + + @Namespace(prefix="CS",reference="http://calendarserver.org/ns/") + public static class GetCTag { + @Text(required=false) + @Getter private String CTag; + } + + public static class GetETag { + @Text(required=false) + @Getter private String ETag; + } + + + /* RFC 5397 WebDAV Current Principal Extension */ + + @Element(required=false,name="current-user-principal") + CurrentUserPrincipal currentUserPrincipal; + + public static class CurrentUserPrincipal { + @Element(required=false) + @Getter private DavHref href; + } + + + /* RFC 3744 WebDAV Access Control Protocol */ + + @ElementList(required=false,name="current-user-privilege-set",entry="privilege") + List currentUserPrivilegeSet; + + public static class Privilege { + @Element(required=false) + @Getter private PrivAll all; + + @Element(required=false) + @Getter private PrivBind bind; + + @Element(required=false) + @Getter private PrivUnbind unbind; + + @Element(required=false) + @Getter private PrivWrite write; + + @Element(required=false,name="write-content") + @Getter private PrivWriteContent writeContent; + + public static class PrivAll { } + public static class PrivBind { } + public static class PrivUnbind { } + public static class PrivWrite { } + public static class PrivWriteContent { } + } + + + + /* RFC 4791 CalDAV, RFC 6352 CardDAV */ + + @Element(required=false,name="addressbook-home-set") + AddressbookHomeSet addressbookHomeSet; + + @Element(required=false,name="calendar-home-set") + CalendarHomeSet calendarHomeSet; + + @Element(required=false,name="addressbook-description") + AddressbookDescription addressbookDescription; + + @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav") + @ElementList(required=false,name="supported-address-data",entry="address-data-type") + List supportedAddressData; + + @Element(required=false,name="calendar-description") + CalendarDescription calendarDescription; + + @Element(required=false,name="calendar-color") + CalendarColor calendarColor; + + @Element(required=false,name="calendar-timezone") + CalendarTimezone calendarTimezone; + + @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") + @ElementList(required=false,name="supported-calendar-component-set",entry="comp") + List supportedCalendarComponentSet; + + @Element(name="address-data",required=false) + AddressData addressData; + + @Element(name="calendar-data",required=false) + CalendarData calendarData; + + + @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav") + public static class AddressbookHomeSet { + @Element(required=false) + @Getter private DavHref href; + } + + @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") + public static class CalendarHomeSet { + @Element(required=false) + @Getter private DavHref href; + } + + @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav") + public static class AddressbookDescription { + @Text(required=false) + @Getter private String description; + } + + @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav") + public static class AddressDataType { + @Attribute(name="content-type") + @Getter private String contentType; + + @Attribute + @Getter private String version; + } + + @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") + public static class CalendarDescription { + @Text(required=false) + @Getter private String description; + } + + @Namespace(prefix="A",reference="http://apple.com/ns/ical/") + public static class CalendarColor { + @Text(required=false) + @Getter private String color; + } + + @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") + public static class CalendarTimezone { + @Text(required=false) + @Getter private String timezone; + } + + @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") + public static class Comp { + @Attribute + @Getter String name; + } + + @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav") + public static class AddressData { + @Text(required=false) + @Getter String vcard; + } + + @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav") + public static class CalendarData { + @Text(required=false) + @Getter String ical; + } +} diff --git a/src/at/bitfire/davdroid/webdav/DavPropfind.java b/src/at/bitfire/davdroid/webdav/DavPropfind.java new file mode 100644 index 00000000..abf4104e --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavPropfind.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Namespace; +import org.simpleframework.xml.Root; + +@Namespace(reference="DAV:") +@Root(name="propfind") +public class DavPropfind { + @Element(required=false) + protected DavProp prop; +} diff --git a/src/at/bitfire/davdroid/webdav/DavPropstat.java b/src/at/bitfire/davdroid/webdav/DavPropstat.java new file mode 100644 index 00000000..74ee66d6 --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavPropstat.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import org.simpleframework.xml.Element; +import org.simpleframework.xml.Root; + +@Root(strict=false,name="propstat") +public class DavPropstat { + @Element + DavProp prop; + + @Element + String status; +} diff --git a/src/at/bitfire/davdroid/webdav/DavRedirectStrategy.java b/src/at/bitfire/davdroid/webdav/DavRedirectStrategy.java new file mode 100644 index 00000000..7019d06c --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavRedirectStrategy.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +import android.util.Log; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.RequestLine; +import ch.boye.httpclientandroidlib.client.RedirectStrategy; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.client.methods.RequestBuilder; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.client.utils.URIUtils; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Custom Redirect Strategy that handles 30x for CalDAV/CardDAV-specific requests correctly + */ +public class DavRedirectStrategy implements RedirectStrategy { + private final static String TAG = "davdroid.DavRedirectStrategy"; + final static DavRedirectStrategy INSTANCE = new DavRedirectStrategy(); + + protected final static String REDIRECTABLE_METHODS[] = { + "OPTIONS", "GET", "PUT", "DELETE" + }; + + + @Override + public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException { + RequestLine line = request.getRequestLine(); + + String location = getLocation(request, response, context).toString(); + Log.i(TAG, "Following redirection: " + line.getMethod() + " " + line.getUri() + " -> " + location); + + return RequestBuilder.copy(request) + .setUri(location) + .removeHeaders("Content-Length") // Content-Length will be set again automatically, if required; + // remove it now to avoid duplicate header + .build(); + } + + /** + * Determines whether a response indicates a redirection and if it does, whether to follow this redirection. + * PROPFIND and REPORT must handle redirections explicitely because multi-status processing requires knowledge of the content location. + * @return true for 3xx responses on OPTIONS, GET, PUT, DELETE requests that have a valid Location header; false otherwise + */ + @Override + public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException { + if (response.getStatusLine().getStatusCode()/100 == 3) { + boolean redirectable = false; + for (String method : REDIRECTABLE_METHODS) + if (method.equalsIgnoreCase(request.getRequestLine().getMethod())) { + redirectable = true; + break; + } + return redirectable && getLocation(request, response, context) != null; + } + return false; + } + + /** + * Gets the destination of a redirection + * @return absolute URL of new location; null if not available + */ + static URL getLocation(HttpRequest request, HttpResponse response, HttpContext context) { + Header locationHdr = response.getFirstHeader("Location"); + if (locationHdr == null) { + Log.e(TAG, "Received redirection without Location header, ignoring"); + return null; + } + try { + URI location = new URI(locationHdr.getValue()); + + // some servers don't return absolute URLs as required by RFC 2616 + if (!location.isAbsolute()) { + Log.w(TAG, "Received invalid redirection to relative URL, repairing"); + URI originalURI = new URI(request.getRequestLine().getUri()); + if (!originalURI.isAbsolute()) { + final HttpHost target = HttpClientContext.adapt(context).getTargetHost(); + if (target != null) + originalURI = URIUtils.rewriteURI(originalURI, target); + else + return null; + } + return new URL(originalURI.toURL(), location.toString()); + } + return location.toURL(); + } catch (URISyntaxException e) { + Log.e(TAG, "Received redirection from/to invalid URI, ignoring", e); + } catch (MalformedURLException e) { + Log.e(TAG, "Received redirection from/to invalid URL, ignoring", e); + } + return null; + } + +} diff --git a/src/at/bitfire/davdroid/webdav/DavResponse.java b/src/at/bitfire/davdroid/webdav/DavResponse.java new file mode 100644 index 00000000..b1ba1fd9 --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/DavResponse.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import java.util.List; + +import lombok.Getter; + +import org.simpleframework.xml.Element; +import org.simpleframework.xml.ElementList; +import org.simpleframework.xml.Root; + +@Root(strict=false) +public class DavResponse { + @Element + @Getter DavHref href; + + @ElementList(inline=true) + @Getter List propstat; +} diff --git a/src/at/bitfire/davdroid/webdav/HttpException.java b/src/at/bitfire/davdroid/webdav/HttpException.java new file mode 100644 index 00000000..d2efd1b7 --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/HttpException.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import lombok.Getter; + +public class HttpException extends ch.boye.httpclientandroidlib.HttpException { + private static final long serialVersionUID = -4805778240079377401L; + + @Getter private int code; + + HttpException(int code, String message) { + super(message); + this.code = code; + } + + public boolean isClientError() { + return code/100 == 4; + } + +} diff --git a/src/at/bitfire/davdroid/webdav/HttpPropfind.java b/src/at/bitfire/davdroid/webdav/HttpPropfind.java new file mode 100644 index 00000000..0e6e211a --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/HttpPropfind.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import java.io.StringWriter; +import java.net.URI; +import java.util.LinkedList; + +import org.simpleframework.xml.Serializer; +import org.simpleframework.xml.core.Persister; + +import android.util.Log; +import ch.boye.httpclientandroidlib.client.methods.HttpEntityEnclosingRequestBase; +import ch.boye.httpclientandroidlib.entity.StringEntity; + +public class HttpPropfind extends HttpEntityEnclosingRequestBase { + private static final String TAG = "davdroid.HttpPropfind"; + + public final static String METHOD_NAME = "PROPFIND"; + + public enum Mode { + CURRENT_USER_PRINCIPAL, + HOME_SETS, + CARDDAV_COLLECTIONS, + CALDAV_COLLECTIONS, + COLLECTION_CTAG, + MEMBERS_ETAG + } + + + HttpPropfind(URI uri) { + setURI(uri); + } + + HttpPropfind(URI uri, Mode mode) { + this(uri); + + DavPropfind propfind = new DavPropfind(); + propfind.prop = new DavProp(); + + int depth = 0; + switch (mode) { + case CURRENT_USER_PRINCIPAL: + propfind.prop.currentUserPrincipal = new DavProp.CurrentUserPrincipal(); + break; + case HOME_SETS: + propfind.prop.addressbookHomeSet = new DavProp.AddressbookHomeSet(); + propfind.prop.calendarHomeSet = new DavProp.CalendarHomeSet(); + break; + case CARDDAV_COLLECTIONS: + depth = 1; + propfind.prop.displayname = new DavProp.DisplayName(); + propfind.prop.resourcetype = new DavProp.ResourceType(); + propfind.prop.currentUserPrivilegeSet = new LinkedList(); + propfind.prop.addressbookDescription = new DavProp.AddressbookDescription(); + propfind.prop.supportedAddressData = new LinkedList(); + break; + case CALDAV_COLLECTIONS: + depth = 1; + propfind.prop.displayname = new DavProp.DisplayName(); + propfind.prop.resourcetype = new DavProp.ResourceType(); + propfind.prop.currentUserPrivilegeSet = new LinkedList(); + propfind.prop.calendarDescription = new DavProp.CalendarDescription(); + propfind.prop.calendarColor = new DavProp.CalendarColor(); + propfind.prop.calendarTimezone = new DavProp.CalendarTimezone(); + propfind.prop.supportedCalendarComponentSet = new LinkedList(); + break; + case COLLECTION_CTAG: + propfind.prop.getctag = new DavProp.GetCTag(); + break; + case MEMBERS_ETAG: + depth = 1; + propfind.prop.getctag = new DavProp.GetCTag(); + propfind.prop.getetag = new DavProp.GetETag(); + break; + } + + try { + Serializer serializer = new Persister(); + StringWriter writer = new StringWriter(); + serializer.write(propfind, writer); + + setHeader("Content-Type", "text/xml; charset=UTF-8"); + setHeader("Accept", "text/xml"); + setHeader("Depth", String.valueOf(depth)); + setEntity(new StringEntity(writer.toString(), "UTF-8")); + } catch(Exception ex) { + Log.e(TAG, "Couldn't prepare PROPFIND request for " + uri, ex); + abort(); + } + } + + @Override + public String getMethod() { + return METHOD_NAME; + } +} diff --git a/src/at/bitfire/davdroid/webdav/HttpReport.java b/src/at/bitfire/davdroid/webdav/HttpReport.java new file mode 100644 index 00000000..044deba5 --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/HttpReport.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import java.net.URI; + +import ch.boye.httpclientandroidlib.client.methods.HttpEntityEnclosingRequestBase; +import ch.boye.httpclientandroidlib.entity.StringEntity; + +public class HttpReport extends HttpEntityEnclosingRequestBase { + + public final static String METHOD_NAME = "REPORT"; + + + HttpReport(URI uri) { + setURI(uri); + } + + HttpReport(URI uri, String entity) { + this(uri); + + setHeader("Content-Type", "text/xml; charset=UTF-8"); + setHeader("Accept", "text/xml"); + setHeader("Depth", "0"); + + setEntity(new StringEntity(entity, "UTF-8")); + } + + @Override + public String getMethod() { + return METHOD_NAME; + } +} diff --git a/src/at/bitfire/davdroid/webdav/NotAuthorizedException.java b/src/at/bitfire/davdroid/webdav/NotAuthorizedException.java new file mode 100644 index 00000000..4eab6adb --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/NotAuthorizedException.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import org.apache.http.HttpStatus; + +public class NotAuthorizedException extends HttpException { + private static final long serialVersionUID = 2490525047224413586L; + + public NotAuthorizedException(String reason) { + super(HttpStatus.SC_UNAUTHORIZED, reason); + } + +} diff --git a/src/at/bitfire/davdroid/webdav/NotFoundException.java b/src/at/bitfire/davdroid/webdav/NotFoundException.java new file mode 100644 index 00000000..f612d733 --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/NotFoundException.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import org.apache.http.HttpStatus; + +public class NotFoundException extends HttpException { + private static final long serialVersionUID = 1565961502781880483L; + + public NotFoundException(String reason) { + super(HttpStatus.SC_NOT_FOUND, reason); + } +} diff --git a/src/at/bitfire/davdroid/webdav/PreconditionFailedException.java b/src/at/bitfire/davdroid/webdav/PreconditionFailedException.java new file mode 100644 index 00000000..064ff38a --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/PreconditionFailedException.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import org.apache.http.HttpStatus; + +public class PreconditionFailedException extends HttpException { + private static final long serialVersionUID = 102282229174086113L; + + public PreconditionFailedException(String reason) { + super(HttpStatus.SC_PRECONDITION_FAILED, reason); + } +} diff --git a/src/at/bitfire/davdroid/webdav/TlsSniSocketFactory.java b/src/at/bitfire/davdroid/webdav/TlsSniSocketFactory.java new file mode 100644 index 00000000..ce5ec2dd --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/TlsSniSocketFactory.java @@ -0,0 +1,186 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import org.apache.commons.lang.StringUtils; +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.net.SSLCertificateSocketFactory; +import android.os.Build; +import android.util.Log; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.conn.socket.LayeredConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.ssl.BrowserCompatHostnameVerifier; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +public class TlsSniSocketFactory implements LayeredConnectionSocketFactory { + private static final String TAG = "davdroid.SNISocketFactory"; + + final static TlsSniSocketFactory INSTANCE = new TlsSniSocketFactory(); + + private final static SSLCertificateSocketFactory sslSocketFactory = + (SSLCertificateSocketFactory)SSLCertificateSocketFactory.getDefault(0); + private final static HostnameVerifier hostnameVerifier = new BrowserCompatHostnameVerifier(); + + + /* + For SSL connections without HTTP(S) proxy: + 1) createSocket() is called + 2) connectSocket() is called which creates a new SSL connection + 2a) SNI is set up, and then + 2b) the connection is established, hands are shaken and certificate/host name are verified + + Layered sockets are used with HTTP(S) proxies: + 1) a new plain socket is created by the HTTP library + 2) the plain socket is connected to http://proxy:8080 + 3) a CONNECT request is sent to the proxy and the response is parsed + 4) now, createLayeredSocket() is called which wraps an SSL socket around the proxy connection, + doing all the set-up and verfication + 4a) Because SSLSocket.createSocket(socket, ...) always does a handshake without allowing + to set up SNI before, *** SNI is not available for layered connections *** (unless + active by Android's defaults, which it isn't at the moment). + */ + + + @Override + public Socket createSocket(HttpContext context) throws IOException { + SSLSocket ssl = (SSLSocket)sslSocketFactory.createSocket(); + setReasonableEncryption(ssl); + return ssl; + } + + @Override + public Socket connectSocket(int timeout, Socket plain, HttpHost host, InetSocketAddress remoteAddr, InetSocketAddress localAddr, HttpContext context) throws IOException { + Log.d(TAG, "Preparing direct SSL connection (without proxy) to " + host); + + // we'll rather use an SSLSocket directly + plain.close(); + + // create a plain SSL socket, but don't do hostname/certificate verification yet + SSLSocket ssl = (SSLSocket)sslSocketFactory.createSocket(remoteAddr.getAddress(), host.getPort()); + setReasonableEncryption(ssl); + + // connect, set SNI, shake hands, verify, print connection info + connectWithSNI(ssl, host.getHostName()); + + return ssl; + } + + @Override + public Socket createLayeredSocket(Socket plain, String host, int port, HttpContext context) throws IOException, UnknownHostException { + Log.d(TAG, "Preparing layered SSL connection (over proxy) to " + host); + + // create a layered SSL socket, but don't do hostname/certificate verification yet + SSLSocket ssl = (SSLSocket)sslSocketFactory.createSocket(plain, host, port, true); + setReasonableEncryption(ssl); + + // already connected, but verify host name again and print some connection info + Log.w(TAG, "Setting SNI/TLSv1.2 will silently fail because the handshake is already done"); + connectWithSNI(ssl, host); + + return ssl; + } + + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + private void connectWithSNI(SSLSocket ssl, String host) throws SSLPeerUnverifiedException { + // - set SNI host name + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + Log.d(TAG, "Using documented SNI with host name " + host); + sslSocketFactory.setHostname(ssl, host); + } else { + Log.d(TAG, "No documented SNI support on Android <4.2, trying with reflection"); + try { + java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class); + setHostnameMethod.invoke(ssl, host); + } catch (Exception e) { + Log.w(TAG, "SNI not useable", e); + } + } + + // verify hostname and certificate + SSLSession session = ssl.getSession(); + if (!hostnameVerifier.verify(host, session)) + throw new SSLPeerUnverifiedException("Cannot verify hostname: " + host); + + Log.d(TAG, "Established " + session.getProtocol() + " connection with " + session.getPeerHost() + + " using " + session.getCipherSuite()); + } + + + @SuppressLint("DefaultLocale") + private void setReasonableEncryption(SSLSocket ssl) { + // set reasonable SSL/TLS settings before the handshake + + // Android 5.0+ (API level21) provides reasonable default settings + // but it still allows SSLv3 + // https://developer.android.com/about/versions/android-5.0-changes.html#ssl + + // - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0, if available) + // - remove all SSL versions (especially SSLv3) because they're insecure now + List protocols = new LinkedList(); + for (String protocol : ssl.getSupportedProtocols()) + if (!protocol.toUpperCase().contains("SSL")) + protocols.add(protocol); + Log.v(TAG, "Setting allowed TLS protocols: " + StringUtils.join(protocols, ", ")); + ssl.setEnabledProtocols(protocols.toArray(new String[0])); + + if (android.os.Build.VERSION.SDK_INT < 21) { + // choose secure cipher suites + List allowedCiphers = Arrays.asList(new String[] { + // allowed secure ciphers according to NIST.SP.800-52r1.pdf Section 3.3.1 (see docs directory) + // TLS 1.2 + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECHDE_RSA_WITH_AES_128_GCM_SHA256", + // maximum interoperability + "TLS_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA", + // additionally + "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + }); + + List availableCiphers = Arrays.asList(ssl.getSupportedCipherSuites()); + + // preferred ciphers = allowed Ciphers \ availableCiphers + HashSet preferredCiphers = new HashSet(allowedCiphers); + preferredCiphers.retainAll(availableCiphers); + + // add preferred ciphers to enabled ciphers + // for maximum security, preferred ciphers should *replace* enabled ciphers, + // but I guess for the security level of DAVdroid, disabling of insecure + // ciphers should be a server-side task + HashSet enabledCiphers = preferredCiphers; + enabledCiphers.addAll(new HashSet(Arrays.asList(ssl.getEnabledCipherSuites()))); + + Log.v(TAG, "Setting allowed TLS ciphers: " + StringUtils.join(enabledCiphers, ", ")); + ssl.setEnabledCipherSuites(enabledCiphers.toArray(new String[0])); + } + } + +} diff --git a/src/at/bitfire/davdroid/webdav/WebDavResource.java b/src/at/bitfire/davdroid/webdav/WebDavResource.java new file mode 100644 index 00000000..92859b7f --- /dev/null +++ b/src/at/bitfire/davdroid/webdav/WebDavResource.java @@ -0,0 +1,584 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import lombok.Cleanup; +import lombok.Getter; +import lombok.ToString; + +import org.apache.commons.lang.StringUtils; +import org.simpleframework.xml.Serializer; +import org.simpleframework.xml.core.Persister; + +import android.util.Log; +import at.bitfire.davdroid.URLUtils; +import at.bitfire.davdroid.resource.Event; +import at.bitfire.davdroid.webdav.DavProp.Comp; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpEntity; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpStatus; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.auth.AuthScope; +import ch.boye.httpclientandroidlib.auth.UsernamePasswordCredentials; +import ch.boye.httpclientandroidlib.client.AuthCache; +import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse; +import ch.boye.httpclientandroidlib.client.methods.HttpDelete; +import ch.boye.httpclientandroidlib.client.methods.HttpGet; +import ch.boye.httpclientandroidlib.client.methods.HttpOptions; +import ch.boye.httpclientandroidlib.client.methods.HttpPut; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.entity.ByteArrayEntity; +import ch.boye.httpclientandroidlib.impl.auth.BasicScheme; +import ch.boye.httpclientandroidlib.impl.client.BasicAuthCache; +import ch.boye.httpclientandroidlib.impl.client.BasicCredentialsProvider; +import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient; +import ch.boye.httpclientandroidlib.message.BasicLineParser; +import ch.boye.httpclientandroidlib.util.EntityUtils; +import ezvcard.VCardVersion; + + +/** + * Represents a WebDAV resource (file or collection). + * This class is used for all CalDAV/CardDAV communcation. + */ +@ToString +public class WebDavResource { + private static final String TAG = "davdroid.WebDavResource"; + + public enum Property { + CURRENT_USER_PRINCIPAL, // resource detection + ADDRESSBOOK_HOMESET, CALENDAR_HOMESET, + CONTENT_TYPE, READ_ONLY, // WebDAV (common) + DISPLAY_NAME, DESCRIPTION, ETAG, + IS_COLLECTION, CTAG, // collections + IS_CALENDAR, COLOR, TIMEZONE, // CalDAV + IS_ADDRESSBOOK, VCARD_VERSION // CardDAV + } + public enum PutMode { + ADD_DONT_OVERWRITE, + UPDATE_DONT_OVERWRITE + } + + // location of this resource + @Getter protected URL location; + + // DAV capabilities (DAV: header) and allowed DAV methods (set for OPTIONS request) + protected Set capabilities = new HashSet(), + methods = new HashSet(); + + // DAV properties + protected HashMap properties = new HashMap(); + @Getter protected List supportedComponents; + + // list of members (only for collections) + @Getter protected List members; + + // content (available after GET) + @Getter protected byte[] content; + + protected CloseableHttpClient httpClient; + protected HttpClientContext context; + + + public WebDavResource(CloseableHttpClient httpClient, URL baseURL) { + this.httpClient = httpClient; + location = baseURL; + + context = HttpClientContext.create(); + context.setCredentialsProvider(new BasicCredentialsProvider()); + } + + public WebDavResource(CloseableHttpClient httpClient, URL baseURL, String username, String password, boolean preemptive) { + this(httpClient, baseURL); + + HttpHost host = new HttpHost(baseURL.getHost(), baseURL.getPort(), baseURL.getProtocol()); + context.getCredentialsProvider().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password)); + + if (preemptive) { + Log.d(TAG, "Using preemptive authentication (not compatible with Digest auth)"); + AuthCache authCache = context.getAuthCache(); + if (authCache == null) + authCache = new BasicAuthCache(); + authCache.put(host, new BasicScheme()); + context.setAuthCache(authCache); + } + } + + WebDavResource(WebDavResource parent) { // copy constructor: based on existing WebDavResource, reuse settings + httpClient = parent.httpClient; + context = parent.context; + location = parent.location; + } + + protected WebDavResource(WebDavResource parent, URL url) { + this(parent); + location = url; + } + + public WebDavResource(WebDavResource parent, String member) throws MalformedURLException { + this(parent); + location = new URL(parent.location, URLUtils.sanitize(member)); + } + + public WebDavResource(WebDavResource parent, String member, String ETag) throws MalformedURLException { + this(parent, member); + properties.put(Property.ETAG, ETag); + } + + + + /* feature detection */ + + public void options() throws URISyntaxException, IOException, HttpException { + HttpOptions options = new HttpOptions(location.toURI()); + CloseableHttpResponse response = httpClient.execute(options, context); + try { + checkResponse(response); + + Header[] allowHeaders = response.getHeaders("Allow"); + for (Header allowHeader : allowHeaders) + methods.addAll(Arrays.asList(allowHeader.getValue().split(", ?"))); + + Header[] capHeaders = response.getHeaders("DAV"); + for (Header capHeader : capHeaders) + capabilities.addAll(Arrays.asList(capHeader.getValue().split(", ?"))); + } finally { + response.close(); + } + } + + public boolean supportsDAV(String capability) { + return capabilities.contains(capability); + } + + public boolean supportsMethod(String method) { + return methods.contains(method); + } + + + /* file hierarchy methods */ + + public String getName() { + String[] names = StringUtils.split(location.getPath(), "/"); + return names[names.length - 1]; + } + + + /* property methods */ + + public String getCurrentUserPrincipal() { + return properties.get(Property.CURRENT_USER_PRINCIPAL); + } + + public String getAddressbookHomeSet() { + return properties.get(Property.ADDRESSBOOK_HOMESET); + } + + public String getCalendarHomeSet() { + return properties.get(Property.CALENDAR_HOMESET); + } + + public String getContentType() { + return properties.get(Property.CONTENT_TYPE); + } + + public void setContentType(String mimeType) { + properties.put(Property.CONTENT_TYPE, mimeType); + } + + public boolean isReadOnly() { + return properties.containsKey(Property.READ_ONLY); + } + + public String getDisplayName() { + return properties.get(Property.DISPLAY_NAME); + } + + public String getDescription() { + return properties.get(Property.DESCRIPTION); + } + + public String getCTag() { + return properties.get(Property.CTAG); + } + public void invalidateCTag() { + properties.remove(Property.CTAG); + } + + public String getETag() { + return properties.get(Property.ETAG); + } + + public boolean isCalendar() { + return properties.containsKey(Property.IS_CALENDAR); + } + + public String getColor() { + return properties.get(Property.COLOR); + } + + public String getTimezone() { + return properties.get(Property.TIMEZONE); + } + + public boolean isAddressBook() { + return properties.containsKey(Property.IS_ADDRESSBOOK); + } + + public VCardVersion getVCardVersion() { + String versionStr = properties.get(Property.VCARD_VERSION); + return (versionStr != null) ? VCardVersion.valueOfByStr(versionStr) : null; + } + + + /* collection operations */ + + public void propfind(HttpPropfind.Mode mode) throws URISyntaxException, IOException, DavException, HttpException { + CloseableHttpResponse response = null; + + // processMultiStatus() requires knowledge of the actual content location, + // so we have to handle redirections manually and create a new request for the new location + for (int i = context.getRequestConfig().getMaxRedirects(); i > 0; i--) { + HttpPropfind propfind = new HttpPropfind(location.toURI(), mode); + response = httpClient.execute(propfind, context); + + if (response.getStatusLine().getStatusCode()/100 == 3) { + location = DavRedirectStrategy.getLocation(propfind, response, context); + Log.i(TAG, "Redirection on PROPFIND; trying again at new content URL: " + location); + // don't forget to throw away the unneeded response content + HttpEntity entity = response.getEntity(); + if (entity != null) { @Cleanup InputStream content = entity.getContent(); } + } else + break; // answer was NOT a redirection, continue + } + if (response == null) + throw new DavNoContentException(); + + try { + checkResponse(response); // will also handle Content-Location + processMultiStatus(response); + } finally { + response.close(); + } + } + + public void multiGet(DavMultiget.Type type, String[] names) throws URISyntaxException, IOException, DavException, HttpException { + CloseableHttpResponse response = null; + + // processMultiStatus() requires knowledge of the actual content location, + // so we have to handle redirections manually and create a new request for the new location + for (int i = context.getRequestConfig().getMaxRedirects(); i > 0; i--) { + // build multi-get XML request + List hrefs = new LinkedList(); + for (String name : names) + hrefs.add(new URL(location, name).getPath()); + DavMultiget multiget = DavMultiget.newRequest(type, hrefs.toArray(new String[0])); + + StringWriter writer = new StringWriter(); + try { + Serializer serializer = new Persister(); + serializer.write(multiget, writer); + } catch (Exception ex) { + Log.e(TAG, "Couldn't create XML multi-get request", ex); + throw new DavException("Couldn't create multi-get request"); + } + + // submit REPORT request + HttpReport report = new HttpReport(location.toURI(), writer.toString()); + response = httpClient.execute(report, context); + + if (response.getStatusLine().getStatusCode()/100 == 3) { + location = DavRedirectStrategy.getLocation(report, response, context); + Log.i(TAG, "Redirection on REPORT multi-get; trying again at new content URL: " + location); + + // don't forget to throw away the unneeded response content + HttpEntity entity = response.getEntity(); + if (entity != null) { @Cleanup InputStream content = entity.getContent(); } + } else + break; // answer was NOT a redirection, continue + } + if (response == null) + throw new DavNoContentException(); + + try { + checkResponse(response); // will also handle Content-Location + processMultiStatus(response); + } finally { + response.close(); + } + } + + + /* resource operations */ + + public void get(String acceptedType) throws URISyntaxException, IOException, HttpException, DavException { + HttpGet get = new HttpGet(location.toURI()); + get.addHeader("Accept", acceptedType); + + CloseableHttpResponse response = httpClient.execute(get, context); + try { + checkResponse(response); + + HttpEntity entity = response.getEntity(); + if (entity == null) + throw new DavNoContentException(); + + content = EntityUtils.toByteArray(entity); + } finally { + response.close(); + } + } + + // returns the ETag of the created/updated resource, if available (null otherwise) + public String put(byte[] data, PutMode mode) throws URISyntaxException, IOException, HttpException { + HttpPut put = new HttpPut(location.toURI()); + put.setEntity(new ByteArrayEntity(data)); + + switch (mode) { + case ADD_DONT_OVERWRITE: + put.addHeader("If-None-Match", "*"); + break; + case UPDATE_DONT_OVERWRITE: + put.addHeader("If-Match", (getETag() != null) ? getETag() : "*"); + break; + } + + if (getContentType() != null) + put.addHeader("Content-Type", getContentType()); + + CloseableHttpResponse response = httpClient.execute(put, context); + try { + checkResponse(response); + + Header eTag = response.getLastHeader("ETag"); + if (eTag != null) + return eTag.getValue(); + } finally { + response.close(); + } + + return null; + } + + public void delete() throws URISyntaxException, IOException, HttpException { + HttpDelete delete = new HttpDelete(location.toURI()); + + if (getETag() != null) + delete.addHeader("If-Match", getETag()); + + CloseableHttpResponse response = httpClient.execute(delete, context); + try { + checkResponse(response); + } finally { + response.close(); + } + } + + + /* helpers */ + + protected void checkResponse(HttpResponse response) throws HttpException { + checkResponse(response.getStatusLine()); + + // handle Content-Location header (see RFC 4918 5.2 Collection Resources) + Header contentLocationHdr = response.getFirstHeader("Content-Location"); + if (contentLocationHdr != null) + try { + // Content-Location was set, update location correspondingly + location = new URL(location, contentLocationHdr.getValue()); + Log.d(TAG, "Set Content-Location to " + location); + } catch (MalformedURLException e) { + Log.w(TAG, "Ignoring invalid Content-Location", e); + } + } + + protected static void checkResponse(StatusLine statusLine) throws HttpException { + int code = statusLine.getStatusCode(); + + if (code/100 == 1 || code/100 == 2) // everything OK + return; + + String reason = code + " " + statusLine.getReasonPhrase(); + switch (code) { + case HttpStatus.SC_UNAUTHORIZED: + throw new NotAuthorizedException(reason); + case HttpStatus.SC_NOT_FOUND: + throw new NotFoundException(reason); + case HttpStatus.SC_PRECONDITION_FAILED: + throw new PreconditionFailedException(reason); + default: + throw new HttpException(code, reason); + } + } + + protected void processMultiStatus(HttpResponse response) throws IOException, HttpException, DavException { + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_MULTI_STATUS) + throw new DavNoMultiStatusException(); + + HttpEntity entity = response.getEntity(); + if (entity == null) + throw new DavNoContentException(); + @Cleanup InputStream content = entity.getContent(); + + DavMultistatus multiStatus; + try { + Serializer serializer = new Persister(); + multiStatus = serializer.read(DavMultistatus.class, content, false); + } catch (Exception ex) { + throw new DavException("Couldn't parse Multi-Status response on REPORT multi-get", ex); + } + + if (multiStatus.response == null) // empty response + throw new DavNoContentException(); + + // member list will be built from response + List members = new LinkedList(); + + // iterate through all resources (either ourselves or member) + for (DavResponse singleResponse : multiStatus.response) { + URL href; + try { + href = new URL(location, URLUtils.sanitize(singleResponse.getHref().href)); + } catch(IllegalArgumentException ex) { + Log.w(TAG, "Ignoring illegal member URI in multi-status response", ex); + continue; + } + Log.d(TAG, "Processing multi-status element: " + href); + + // process known properties + HashMap properties = new HashMap(); + List supportedComponents = null; + byte[] data = null; + + for (DavPropstat singlePropstat : singleResponse.getPropstat()) { + StatusLine status = BasicLineParser.parseStatusLine(singlePropstat.status, new BasicLineParser()); + + // ignore information about missing properties etc. + if (status.getStatusCode()/100 != 1 && status.getStatusCode()/100 != 2) + continue; + DavProp prop = singlePropstat.prop; + + if (prop.currentUserPrincipal != null && prop.currentUserPrincipal.getHref() != null) + properties.put(Property.CURRENT_USER_PRINCIPAL, prop.currentUserPrincipal.getHref().href); + + if (prop.currentUserPrivilegeSet != null) { + // privilege info available + boolean mayAll = false, + mayBind = false, + mayUnbind = false, + mayWrite = false, + mayWriteContent = false; + for (DavProp.Privilege privilege : prop.currentUserPrivilegeSet) { + if (privilege.getAll() != null) mayAll = true; + if (privilege.getBind() != null) mayBind = true; + if (privilege.getUnbind() != null) mayUnbind = true; + if (privilege.getWrite() != null) mayWrite = true; + if (privilege.getWriteContent() != null) mayWriteContent = true; + } + if (!mayAll && !mayWrite && !(mayWriteContent && mayBind && mayUnbind)) + properties.put(Property.READ_ONLY, "1"); + } + + if (prop.addressbookHomeSet != null && prop.addressbookHomeSet.getHref() != null) + properties.put(Property.ADDRESSBOOK_HOMESET, URLUtils.ensureTrailingSlash(prop.addressbookHomeSet.getHref().href)); + + if (prop.calendarHomeSet != null && prop.calendarHomeSet.getHref() != null) + properties.put(Property.CALENDAR_HOMESET, URLUtils.ensureTrailingSlash(prop.calendarHomeSet.getHref().href)); + + if (prop.displayname != null) + properties.put(Property.DISPLAY_NAME, prop.displayname.getDisplayName()); + + if (prop.resourcetype != null) { + if (prop.resourcetype.getCollection() != null) { + properties.put(Property.IS_COLLECTION, "1"); + // is a collection, ensure trailing slash + href = URLUtils.ensureTrailingSlash(href); + } + if (prop.resourcetype.getAddressbook() != null) { // CardDAV collection properties + properties.put(Property.IS_ADDRESSBOOK, "1"); + + if (prop.addressbookDescription != null) + properties.put(Property.DESCRIPTION, prop.addressbookDescription.getDescription()); + if (prop.supportedAddressData != null) + for (DavProp.AddressDataType dataType : prop.supportedAddressData) + if ("text/vcard".equalsIgnoreCase(dataType.getContentType())) + // ignore "3.0" as it MUST be supported anyway + if ("4.0".equals(dataType.getVersion())) + properties.put(Property.VCARD_VERSION, VCardVersion.V4_0.getVersion()); + } + if (prop.resourcetype.getCalendar() != null) { // CalDAV collection propertioes + properties.put(Property.IS_CALENDAR, "1"); + + if (prop.calendarDescription != null) + properties.put(Property.DESCRIPTION, prop.calendarDescription.getDescription()); + + if (prop.calendarColor != null) + properties.put(Property.COLOR, prop.calendarColor.getColor()); + + if (prop.calendarTimezone != null) + try { + properties.put(Property.TIMEZONE, Event.TimezoneDefToTzId(prop.calendarTimezone.getTimezone())); + } catch(IllegalArgumentException e) { + } + + if (prop.supportedCalendarComponentSet != null) { + supportedComponents = new LinkedList(); + for (Comp component : prop.supportedCalendarComponentSet) + supportedComponents.add(component.getName()); + } + } + } + + if (prop.getctag != null) + properties.put(Property.CTAG, prop.getctag.getCTag()); + + if (prop.getetag != null) + properties.put(Property.ETAG, prop.getetag.getETag()); + + if (prop.calendarData != null && prop.calendarData.ical != null) + data = prop.calendarData.ical.getBytes(); + else if (prop.addressData != null && prop.addressData.vcard != null) + data = prop.addressData.vcard.getBytes(); + } + + // about which resource is this response? + if (location.equals(href) || URLUtils.ensureTrailingSlash(location).equals(href)) { // about ourselves + this.properties.putAll(properties); + if (supportedComponents != null) + this.supportedComponents = supportedComponents; + this.content = data; + + } else { // about a member + WebDavResource member = new WebDavResource(this, href); + member.properties = properties; + member.supportedComponents = supportedComponents; + member.content = data; + + members.add(member); + } + } + + this.members = members; + } + +} diff --git a/src/ical4j.properties b/src/ical4j.properties new file mode 100644 index 00000000..6db152a4 --- /dev/null +++ b/src/ical4j.properties @@ -0,0 +1,6 @@ + +net.fortuna.ical4j.timezone.update.enabled=false + +ical4j.unfolding.relaxed=true +ical4j.parsing.relaxed=true +ical4j.compatibility.outlook=true diff --git a/test/AndroidManifest.xml b/test/AndroidManifest.xml new file mode 100644 index 00000000..d63a92b4 --- /dev/null +++ b/test/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/test/assets/all-day-0sec.ics b/test/assets/all-day-0sec.ics new file mode 100644 index 00000000..07679f29 --- /dev/null +++ b/test/assets/all-day-0sec.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//hacksw/handcal//NONSGML v1.0//EN +BEGIN:VEVENT +UID:all-day-0sec@example.com +DTSTAMP:20140101T000000Z +DTSTART;VALUE=DATE:19970714 +DTEND;VALUE=DATE:19970714 +SUMMARY:0 Sec Event +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/test/assets/all-day-10days.ics b/test/assets/all-day-10days.ics new file mode 100644 index 00000000..52e6dbdf --- /dev/null +++ b/test/assets/all-day-10days.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//hacksw/handcal//NONSGML v1.0//EN +BEGIN:VEVENT +UID:all-day-10days@example.com +DTSTAMP:20140101T000000Z +DTSTART;VALUE=DATE:19970714 +DTEND;VALUE=DATE:19970724 +SUMMARY:All-Day 10 Days +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/test/assets/all-day-1day.ics b/test/assets/all-day-1day.ics new file mode 100644 index 00000000..ead04361 --- /dev/null +++ b/test/assets/all-day-1day.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//hacksw/handcal//NONSGML v1.0//EN +BEGIN:VEVENT +UID:all-day-1day@example.com +DTSTAMP:20140101T000000Z +DTSTART;VALUE=DATE:19970714 +DTEND;VALUE=DATE:19970714 +SUMMARY:All-Day 1 Day +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/test/assets/event-on-that-day.ics b/test/assets/event-on-that-day.ics new file mode 100644 index 00000000..0ccbd4f3 --- /dev/null +++ b/test/assets/event-on-that-day.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//hacksw/handcal//NONSGML v1.0//EN +BEGIN:VEVENT +UID:event-on-that-day@example.com +DTSTAMP:19970714T170000Z +ORGANIZER;CN=John Doe:MAILTO:john.doe@example.com +DTSTART;VALUE=DATE:19970714 +SUMMARY:Bastille Day Party +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/test/assets/impp.vcf b/test/assets/impp.vcf new file mode 100644 index 00000000..c08f9029 --- /dev/null +++ b/test/assets/impp.vcf @@ -0,0 +1,9 @@ +BEGIN:VCARD +VERSION:3.0 +UID:2de59c6cc9 +PRODID:-//ownCloud//NONSGML Contacts 0.2.5//EN +REV:2013-12-08T00:04:30+00:00 +FN:test mctest +N:mctest;test;;; +IMPP;TYPE=WORK;X-SERVICE-TYPE=jabber:test-without-valid-scheme@test.tld +END:VCARD diff --git a/test/assets/invalid-unknown-properties.vcf b/test/assets/invalid-unknown-properties.vcf new file mode 100644 index 00000000..3fd77fce --- /dev/null +++ b/test/assets/invalid-unknown-properties.vcf @@ -0,0 +1,5 @@ +BEGIN:VCARD +VERSION:3.0 +FN:VCard with invalid unknown properties +X-UNKNOWN@PROPERTY:MUST-NOT_CONTAIN?OTHER*LETTERS; +END:VCARD \ No newline at end of file diff --git a/test/assets/reference.vcf b/test/assets/reference.vcf new file mode 100644 index 00000000..ba6a6b4d --- /dev/null +++ b/test/assets/reference.vcf @@ -0,0 +1,16 @@ +BEGIN:VCARD +VERSION:3.0 +N:Gump;Forrest;Mr. +FN:Forrest Gump +ORG:Bubba Gump Shrimp Co. +TITLE:Shrimp Man +PHOTO;VALUE=URL;TYPE=GIF:http://www.example.com/dir_photos/my_photo.gif +TEL;TYPE=WORK,VOICE:(111) 555-1212 +TEL;TYPE=HOME,VOICE:(404) 555-1212 +ADR;TYPE=WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America +LABEL;TYPE=WORK:100 Waters Edge\nBaytown, LA 30314\nUnited States of America +ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America +LABEL;TYPE=HOME:42 Plantation St.\nBaytown, LA 30314\nUnited States of America +EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com +REV:2008-04-24T19:52:43Z +END:VCARD \ No newline at end of file diff --git a/test/assets/test.random b/test/assets/test.random new file mode 100644 index 00000000..eb3e5b02 Binary files /dev/null and b/test/assets/test.random differ diff --git a/test/assets/vcard3-sample1.vcf b/test/assets/vcard3-sample1.vcf new file mode 100644 index 00000000..f5c9f977 --- /dev/null +++ b/test/assets/vcard3-sample1.vcf @@ -0,0 +1,16 @@ +BEGIN:VCARD +VERSION:3.0 +N:Gump;Forrest +FN:Forrest Gump +ORG:Bubba Gump Shrimp Co. +TITLE:Shrimp Man +PHOTO;VALUE=URL;TYPE=GIF:http://www.example.com/dir_photos/my_photo.gif +TEL;TYPE=WORK,VOICE:(111) 555-1212 +TEL;TYPE=HOME,VOICE:(404) 555-1212 +ADR;TYPE=WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America +LABEL;TYPE=WORK:100 Waters Edge\nBaytown, LA 30314\nUnited States of America +ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America +LABEL;TYPE=HOME:42 Plantation St.\nBaytown, LA 30314\nUnited States of America +EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com +REV:2008-04-24T19:52:43Z +END:VCARD \ No newline at end of file diff --git a/test/assets/vienna-evolution.ics b/test/assets/vienna-evolution.ics new file mode 100644 index 00000000..5f11911a --- /dev/null +++ b/test/assets/vienna-evolution.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +PRODID:-//Ximian//NONSGML Evolution Calendar//EN +VERSION:2.0 +METHOD:PUBLISH +BEGIN:VTIMEZONE +TZID:/freeassociation.sourceforge.net/Tzfile/Europe/Vienna +X-LIC-LOCATION:Europe/Vienna +BEGIN:STANDARD +TZNAME:CET +DTSTART:19701027T030000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +END:STANDARD +BEGIN:DAYLIGHT +TZNAME:CEST +DTSTART:19700331T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +UID:c252087c-7354-4722-aea9-0e7d86c01a25 +DTSTAMP:20130926T151211Z +SUMMARY:Test-Ereignis im schönen Wien +DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/Europe/Vienna:20131009T170000 +DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/Europe/Vienna:20131009T180000 +X-RADICALE-NAME:97929342-291a-434e-bf1a-fa1749bf99d0.ics +X-EVOLUTION-CALDAV-HREF:/radicale/rfc2822/default.ics/97929342-291a-434e-bf1a-fa1749bf99d0.ics +X-EVOLUTION-CALDAV-ETAG:\"-3264224243575339985\" +END:VEVENT +END:VCALENDAR diff --git a/test/proguard-project.txt b/test/proguard-project.txt new file mode 100644 index 00000000..f2fe1559 --- /dev/null +++ b/test/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/test/project.properties b/test/project.properties new file mode 100644 index 00000000..4ab12569 --- /dev/null +++ b/test/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-19 diff --git a/test/res/drawable-hdpi/ic_launcher.png b/test/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 00000000..96a442e5 Binary files /dev/null and b/test/res/drawable-hdpi/ic_launcher.png differ diff --git a/test/res/drawable-ldpi/ic_launcher.png b/test/res/drawable-ldpi/ic_launcher.png new file mode 100644 index 00000000..99238729 Binary files /dev/null and b/test/res/drawable-ldpi/ic_launcher.png differ diff --git a/test/res/drawable-mdpi/ic_launcher.png b/test/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 00000000..359047df Binary files /dev/null and b/test/res/drawable-mdpi/ic_launcher.png differ diff --git a/test/res/drawable-xhdpi/ic_launcher.png b/test/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 00000000..71c6d760 Binary files /dev/null and b/test/res/drawable-xhdpi/ic_launcher.png differ diff --git a/test/res/values/strings.xml b/test/res/values/strings.xml new file mode 100644 index 00000000..2539643a --- /dev/null +++ b/test/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + DavdroidTest + + diff --git a/test/robohydra/.gitignore b/test/robohydra/.gitignore new file mode 100644 index 00000000..3c3629e6 --- /dev/null +++ b/test/robohydra/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/test/robohydra/davdroid.conf b/test/robohydra/davdroid.conf new file mode 100644 index 00000000..37807fd7 --- /dev/null +++ b/test/robohydra/davdroid.conf @@ -0,0 +1,6 @@ +{"plugins":[ + "assets", + "redirect", + "dav", + "dav-invalid" +]} diff --git a/test/robohydra/plugins/assets/index.js b/test/robohydra/plugins/assets/index.js new file mode 100644 index 00000000..2a254b9a --- /dev/null +++ b/test/robohydra/plugins/assets/index.js @@ -0,0 +1,12 @@ +var RoboHydraHeadFilesystem = require("robohydra").heads.RoboHydraHeadFilesystem; + +exports.getBodyParts = function(conf) { + return { + heads: [ + new RoboHydraHeadFilesystem({ + mountPath: '/assets/', + documentRoot: '../assets' + }) + ] + }; +}; diff --git a/test/robohydra/plugins/dav-invalid/index.js b/test/robohydra/plugins/dav-invalid/index.js new file mode 100644 index 00000000..b14ee944 --- /dev/null +++ b/test/robohydra/plugins/dav-invalid/index.js @@ -0,0 +1,51 @@ +var roboHydraHeadDAV = require("../headdav"); + +exports.getBodyParts = function(conf) { + return { + heads: [ + /* address-book home set */ + new RoboHydraHeadDAV({ + path: "/dav-invalid/addressbooks/user%40domain/", + handler: function(req,res,next) { + if (req.method == "PROPFIND" && req.rawBody.toString().match(/addressbook-description/)) { + res.statusCode = 207; + res.write('\\ + \ + \ + /dav/addressbooks/user@domain/My Contacts:1.vcf/\ + \ + \ + \ + \ + \ + \ + \ + Address Book with dubious characters in path\ + \ + \ + HTTP/1.1 200 OK\ + \ + \ + \ + HTTPS://example.com/user@domain/absolute-url.vcf\ + \ + \ + \ + \ + \ + \ + \ + Address Book with absolute URL and at sign in path\ + \ + \ + HTTP/1.1 200 OK\ + \ + \ + \ + '); + } + } + }) + ] + }; +}; diff --git a/test/robohydra/plugins/dav/index.js b/test/robohydra/plugins/dav/index.js new file mode 100644 index 00000000..bdf7cee3 --- /dev/null +++ b/test/robohydra/plugins/dav/index.js @@ -0,0 +1,269 @@ +var roboHydraHeadDAV = require("../headdav"); + +exports.getBodyParts = function(conf) { + return { + heads: [ + /* base URL, provide default DAV here */ + new RoboHydraHeadDAV({ path: "/dav/" }), + + /* multistatus parsing */ + new RoboHydraHeadDAV({ + path: "/dav/collection-response-with-trailing-slash", + handler: function(req,res,next) { + if (req.method == "PROPFIND") { + res.statusCode = 207; + res.write('\\ + \ + \ + /dav/collection-response-with-trailing-slash/ \ + \ + \ + \ + /principals/ok\ + \ + \ + \ + \ + \ + HTTP/1.1 200 OK\ + \ + \ + \ + '); + } + } + }), + new RoboHydraHeadDAV({ + path: "/dav/collection-response-without-trailing-slash", + handler: function(req,res,next) { + if (req.method == "PROPFIND") { + res.statusCode = 207; + res.write('\\ + \ + \ + /dav/collection-response-without-trailing-slash \ + \ + \ + \ + /principals/ok\ + \ + \ + \ + \ + \ + HTTP/1.1 200 OK\ + \ + \ + \ + '); + } + } + }), + + /* principal URL */ + new RoboHydraHeadDAV({ + path: "/dav/principals/users/test", + handler: function(req,res,next) { + if (req.method == "PROPFIND" && req.rawBody.toString().match(/home-?set/)) { + res.statusCode = 207; + res.write('\\ + \ + \ + ' + req.url + ' \ + \ + \ + \ + /dav/addressbooks/test\ + \ + \ + /dav/calendars/test/\ + \ + \ + HTTP/1.1 200 OK\ + \ + \ + \ + '); + } + } + }), + + /* address-book home set */ + new RoboHydraHeadDAV({ + path: "/dav/addressbooks/test/", + handler: function(req,res,next) { + if (!req.url.match(/\/$/)) { + res.statusCode = 302; + res.headers['location'] = "/dav/addressbooks/test/"; + } + else if (req.method == "PROPFIND" && req.rawBody.toString().match(/addressbook-description/)) { + res.statusCode = 207; + res.write('\\ + \ + \ + /dav/addressbooks/test/useless-member\ + \ + \ + \ + \ + HTTP/1.1 200 OK\ + \ + \ + \ + /dav/addressbooks/test/default-v4.vcf/\ + \ + \ + \ + \ + \ + \ + Default Address Book\ + \ + \ + \ + \ + \ + HTTP/1.1 200 OK\ + \ + \ + \ + '); + } + } + }), + + /* calendar home set */ + new RoboHydraHeadDAV({ + path: "/dav/calendars/test/", + handler: function(req,res,next) { + if (req.method == "PROPFIND" && req.rawBody.toString().match(/calendar-description/)) { + res.statusCode = 207; + res.write('\\ + \ + \ + /dav/calendars/test/shared.forbidden\ + \ + \ + \ + \ + HTTP/1.1 403 Forbidden\ + \ + \ + \ + /dav/calendars/test/private.ics\ + \ + \ + \ + \ + \ + \ + Private Calendar\ + This is my private calendar.\ + \ + HTTP/1.1 200 OK\ + \ + \ + \ + /dav/calendars/test/work.ics\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + Work Calendar\ + 0xFF00FF\ + \ + HTTP/1.1 200 OK\ + \ + \ + \ + '); + } + } + }), + + /* non-existing file */ + new RoboHydraHeadDAV({ + path: "/dav/collection/new.file", + handler: function(req,res,next) { + if (req.method == "PUT") { + if (req.headers['if-match']) /* can't overwrite new file */ + res.statusCode = 412; + else { + res.statusCode = 201; + res.headers["ETag"] = "has-just-been-created"; + } + + } else if (req.method == "DELETE") + res.statusCode = 404; + } + }), + + /* existing file */ + new RoboHydraHeadDAV({ + path: "/dav/collection/existing.file", + handler: function(req,res,next) { + if (req.method == "PUT") { + if (req.headers['if-none-match']) /* requested "don't overwrite", but this file exists */ + res.statusCode = 412; + else { + res.statusCode = 204; + res.headers["ETag"] = "has-just-been-updated"; + } + + } else if (req.method == "DELETE") + res.statusCode = 204; + } + }), + + /* address-book multiget */ + new RoboHydraHeadDAV({ + path: "/dav/addressbooks/default.vcf/", + handler: function(req,res,next) { + if (req.method == "REPORT" && req.rawBody.toString().match(/addressbook-multiget[\s\S]+[\s\S]+/m)) { + res.statusCode = 207; + res.write('\\ + \ + \ + /dav/addressbooks/default.vcf/1.vcf\ + \ + \ + \ + BEGIN:VCARD\ + VERSION:3.0\ + NICKNAME:MULTIGET1\ + UID:1\ + END:VCARD\ + \ + \ + HTTP/1.1 200 OK\ + \ + \ + \ + /dav/addressbooks/default.vcf/2.vcf\ + \ + \ + \ + BEGIN:VCARD\ + VERSION:3.0\ + NICKNAME:MULTIGET2\ + UID:2\ + END:VCARD\ + \ + \ + HTTP/1.1 200 OK\ + \ + \ + \ + '); + } + } + }), + + ] + }; +}; diff --git a/test/robohydra/plugins/headdav.js b/test/robohydra/plugins/headdav.js new file mode 100644 index 00000000..613a2678 --- /dev/null +++ b/test/robohydra/plugins/headdav.js @@ -0,0 +1,57 @@ +var roboHydra = require("robohydra"), + roboHydraHeads = roboHydra.heads, + roboHydraHead = roboHydraHeads.RoboHydraHead; + +RoboHydraHeadDAV = roboHydraHeads.roboHydraHeadType({ + name: 'WebDAV Server', + mandatoryProperties: [ 'path' ], + optionalProperties: [ 'handler' ], + + parentPropBuilder: function() { + var myHandler = this.handler; + return { + path: this.path, + handler: function(req,res,next) { + // default DAV behavior + res.headers['DAV'] = 'addressbook, calendar-access'; + res.statusCode = 500; + + // verify Accept header + var accept = req.headers['accept']; + if (req.method == "GET" && (accept == undefined || !accept.match(/text\/(calendar|vcard|xml)/)) || + (req.method == "PROPFIND" || req.method == "REPORT") && (accept == undefined || accept != "text/xml")) + res.statusCode = 406; + + // DAV operations that work on all URLs + else if (req.method == "OPTIONS") { + res.statusCode = 204; + res.headers['Allow'] = 'OPTIONS, PROPFIND, GET, PUT, DELETE, REPORT'; + + } else if (req.method == "PROPFIND" && req.rawBody.toString().match(/current-user-principal/)) { + res.statusCode = 207; + res.write('\\ + \ + \ + ' + req.url + ' \ + \ + \ + \ + /dav/principals/users/test\ + \ + \ + HTTP/1.1 200 OK\ + \ + \ + \ + '); + + } else if (typeof myHandler != 'undefined') + myHandler(req,res,next); + + res.end(); + } + } + } +}); + +module.exports = RoboHydraHeadDAV; diff --git a/test/robohydra/plugins/redirect/index.js b/test/robohydra/plugins/redirect/index.js new file mode 100644 index 00000000..4c2cbab0 --- /dev/null +++ b/test/robohydra/plugins/redirect/index.js @@ -0,0 +1,57 @@ +require('../simple'); + +var RoboHydraHead = require('robohydra').heads.RoboHydraHead; + +exports.getBodyParts = function(conf) { + return { + heads: [ + // well-known URIs + new SimpleResponseHead({ + path: '/.well-known/caldav', + status: 302, + headers: { Location: '/dav/' } + }), + new SimpleResponseHead({ + path: '/.well-known/carddav', + status: 302, + headers: { Location: '/dav/' } + }), + + // generic redirections + new RoboHydraHead({ + path: '/redirect/301', + handler: function(req,res,next) { + res.statusCode = 301; + var location = req.queryParams['to'] || '/assets/test.random'; + res.headers = { + Location: location + } + res.end(); + } + }), + new RoboHydraHead({ + path: '/redirect/302', + handler: function(req,res,next) { + res.statusCode = 302; + var location = req.queryParams['to'] || '/assets/test.random'; + res.headers = { + Location: location + } + res.end(); + } + }), + + // special redirections + new SimpleResponseHead({ + path: '/redirect/relative', + status: 302, + headers: { Location: '/new/location' } + }), + new SimpleResponseHead({ + path: '/redirect/without-location', + status: 302 + }) + + ] + }; +}; diff --git a/test/robohydra/plugins/simple.js b/test/robohydra/plugins/simple.js new file mode 100644 index 00000000..3d506ee2 --- /dev/null +++ b/test/robohydra/plugins/simple.js @@ -0,0 +1,28 @@ +var roboHydra = require("robohydra"), + roboHydraHeads = roboHydra.heads, + roboHydraHead = roboHydraHeads.RoboHydraHead; + +SimpleResponseHead = roboHydraHeads.roboHydraHeadType({ + name: 'Simple HTTP Response', + mandatoryProperties: [ 'path', 'status' ], + optionalProperties: [ 'headers', 'body' ], + + parentPropBuilder: function() { + var head = this; + return { + path: this.path, + handler: function(req,res,next) { + res.statusCode = head.status; + if (typeof head.headers != 'undefined') + res.headers = head.headers; + if (typeof head.body != 'undefined') + res.write(head.body); + else + res.write(); + res.end(); + } + } + } +}); + +module.exports = SimpleResponseHead; diff --git a/test/robohydra/run.sh b/test/robohydra/run.sh new file mode 100755 index 00000000..f53c7967 --- /dev/null +++ b/test/robohydra/run.sh @@ -0,0 +1,2 @@ +#!/bin/sh +node_modules/robohydra/bin/robohydra.js davdroid.conf -I plugins diff --git a/test/src/at/bitfire/davdroid/resource/test/ContactTest.java b/test/src/at/bitfire/davdroid/resource/test/ContactTest.java new file mode 100644 index 00000000..26ee6b1b --- /dev/null +++ b/test/src/at/bitfire/davdroid/resource/test/ContactTest.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource.test; + +import java.io.IOException; +import java.io.InputStream; + +import ezvcard.property.Email; +import ezvcard.property.Telephone; +import lombok.Cleanup; +import android.content.res.AssetManager; +import android.test.InstrumentationTestCase; +import at.bitfire.davdroid.resource.Contact; +import at.bitfire.davdroid.resource.InvalidResourceException; + +public class ContactTest extends InstrumentationTestCase { + AssetManager assetMgr; + + public void setUp() throws IOException, InvalidResourceException { + assetMgr = getInstrumentation().getContext().getResources().getAssets(); + } + + public void testReferenceVCard() throws IOException, InvalidResourceException { + Contact c = parseVCF("reference.vcf"); + assertEquals("Gump", c.getFamilyName()); + assertEquals("Forrest", c.getGivenName()); + assertEquals("Forrest Gump", c.getDisplayName()); + assertEquals("Bubba Gump Shrimp Co.", c.getOrganization().getValues().get(0)); + assertEquals("Shrimp Man", c.getJobTitle()); + + Telephone phone1 = c.getPhoneNumbers().get(0); + assertEquals("(111) 555-1212", phone1.getText()); + assertEquals("WORK", phone1.getParameters("TYPE").get(0)); + assertEquals("VOICE", phone1.getParameters("TYPE").get(1)); + + Telephone phone2 = c.getPhoneNumbers().get(1); + assertEquals("(404) 555-1212", phone2.getText()); + assertEquals("HOME", phone2.getParameters("TYPE").get(0)); + assertEquals("VOICE", phone2.getParameters("TYPE").get(1)); + + Email email = c.getEmails().get(0); + assertEquals("forrestgump@example.com", email.getValue()); + assertEquals("PREF", email.getParameters("TYPE").get(0)); + assertEquals("INTERNET", email.getParameters("TYPE").get(1)); + } + + public void testParseInvalidUnknownProperties() throws IOException, InvalidResourceException { + Contact c = parseVCF("invalid-unknown-properties.vcf"); + assertEquals("VCard with invalid unknown properties", c.getDisplayName()); + assertNull(c.getUnknownProperties()); + } + + + protected Contact parseVCF(String fname) throws IOException, InvalidResourceException { + @Cleanup InputStream in = assetMgr.open(fname, AssetManager.ACCESS_STREAMING); + Contact c = new Contact(fname, null); + c.parseEntity(in); + return c; + } +} diff --git a/test/src/at/bitfire/davdroid/resource/test/EventTest.java b/test/src/at/bitfire/davdroid/resource/test/EventTest.java new file mode 100644 index 00000000..89afdc1c --- /dev/null +++ b/test/src/at/bitfire/davdroid/resource/test/EventTest.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource.test; + +import java.io.IOException; +import java.io.InputStream; + +import lombok.Cleanup; +import net.fortuna.ical4j.data.ParserException; +import android.content.res.AssetManager; +import android.test.InstrumentationTestCase; +import android.text.format.Time; +import at.bitfire.davdroid.resource.Event; +import at.bitfire.davdroid.resource.InvalidResourceException; + +public class EventTest extends InstrumentationTestCase { + AssetManager assetMgr; + + Event eViennaEvolution, + eOnThatDay, eAllDay1Day, eAllDay10Days, eAllDay0Sec; + + public void setUp() throws IOException, InvalidResourceException { + assetMgr = getInstrumentation().getContext().getResources().getAssets(); + + eViennaEvolution = parseCalendar("vienna-evolution.ics"); + eOnThatDay = parseCalendar("event-on-that-day.ics"); + eAllDay1Day = parseCalendar("all-day-1day.ics"); + eAllDay10Days = parseCalendar("all-day-10days.ics"); + eAllDay0Sec = parseCalendar("all-day-0sec.ics"); + + //assertEquals("Test-Ereignis im schönen Wien", e.getSummary()); + } + + + public void testStartEndTimes() throws IOException, ParserException { + // event with start+end date-time + assertEquals(1381330800000L, eViennaEvolution.getDtStartInMillis()); + assertEquals("Europe/Vienna", eViennaEvolution.getDtStartTzID()); + assertEquals(1381334400000L, eViennaEvolution.getDtEndInMillis()); + assertEquals("Europe/Vienna", eViennaEvolution.getDtEndTzID()); + } + + public void testStartEndTimesAllDay() throws IOException, ParserException { + // event with start date only + assertEquals(868838400000L, eOnThatDay.getDtStartInMillis()); + assertEquals(Time.TIMEZONE_UTC, eOnThatDay.getDtStartTzID()); + // DTEND missing in VEVENT, must have been set to DTSTART+1 day + assertEquals(868838400000L + 86400000, eOnThatDay.getDtEndInMillis()); + assertEquals(Time.TIMEZONE_UTC, eOnThatDay.getDtEndTzID()); + + // event with start+end date for all-day event (one day) + assertEquals(868838400000L, eAllDay1Day.getDtStartInMillis()); + assertEquals(Time.TIMEZONE_UTC, eAllDay1Day.getDtStartTzID()); + assertEquals(868838400000L + 86400000, eAllDay1Day.getDtEndInMillis()); + assertEquals(Time.TIMEZONE_UTC, eAllDay1Day.getDtEndTzID()); + + // event with start+end date for all-day event (ten days) + assertEquals(868838400000L, eAllDay10Days.getDtStartInMillis()); + assertEquals(Time.TIMEZONE_UTC, eAllDay10Days.getDtStartTzID()); + assertEquals(868838400000L + 10*86400000, eAllDay10Days.getDtEndInMillis()); + assertEquals(Time.TIMEZONE_UTC, eAllDay10Days.getDtEndTzID()); + + // event with start+end date on some day (invalid 0 sec-event) + assertEquals(868838400000L, eAllDay0Sec.getDtStartInMillis()); + assertEquals(Time.TIMEZONE_UTC, eAllDay0Sec.getDtStartTzID()); + // DTEND invalid in VEVENT, must have been set to DTSTART+1 day + assertEquals(868838400000L + 86400000, eAllDay0Sec.getDtEndInMillis()); + assertEquals(Time.TIMEZONE_UTC, eAllDay0Sec.getDtEndTzID()); + } + + public void testTimezoneDefToTzId() { + // test valid definition + final String VTIMEZONE_SAMPLE = // taken from RFC 4791, 5.2.2. CALDAV:calendar-timezone Property + "BEGIN:VCALENDAR\n" + + "PRODID:-//Example Corp.//CalDAV Client//EN\n" + + "VERSION:2.0\n" + + "BEGIN:VTIMEZONE\n" + + "TZID:US-Eastern\n" + + "LAST-MODIFIED:19870101T000000Z\n" + + "BEGIN:STANDARD\n" + + "DTSTART:19671029T020000\n" + + "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\n" + + "TZOFFSETFROM:-0400\n" + + "TZOFFSETTO:-0500\n" + + "TZNAME:Eastern Standard Time (US & Canada)\n" + + "END:STANDARD\n" + + "BEGIN:DAYLIGHT\n" + + "DTSTART:19870405T020000\n" + + "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4\n" + + "TZOFFSETFROM:-0500\n" + + "TZOFFSETTO:-0400\n" + + "TZNAME:Eastern Daylight Time (US & Canada)\n" + + "END:DAYLIGHT\n" + + "END:VTIMEZONE\n" + + "END:VCALENDAR"; + assertEquals("US-Eastern", Event.TimezoneDefToTzId(VTIMEZONE_SAMPLE)); + + // test null value + try { + Event.TimezoneDefToTzId(null); + fail(); + } catch(IllegalArgumentException e) { + assert(true); + } + + // test invalid time zone + try { + Event.TimezoneDefToTzId("/* invalid content */"); + fail(); + } catch(IllegalArgumentException e) { + assert(true); + } + } + + + protected Event parseCalendar(String fname) throws IOException, InvalidResourceException { + @Cleanup InputStream in = assetMgr.open(fname, AssetManager.ACCESS_STREAMING); + Event e = new Event(fname, null); + e.parseEntity(in); + return e; + } +} diff --git a/test/src/at/bitfire/davdroid/resource/test/LocalCalendarTest.java b/test/src/at/bitfire/davdroid/resource/test/LocalCalendarTest.java new file mode 100644 index 00000000..6fe5afea --- /dev/null +++ b/test/src/at/bitfire/davdroid/resource/test/LocalCalendarTest.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.resource.test; + +import java.util.Calendar; + +import lombok.Cleanup; +import android.accounts.Account; +import android.annotation.TargetApi; +import android.content.ContentProviderClient; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.RemoteException; +import android.provider.CalendarContract; +import android.provider.CalendarContract.Attendees; +import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Events; +import android.provider.CalendarContract.Reminders; +import android.test.InstrumentationTestCase; +import android.util.Log; +import at.bitfire.davdroid.resource.LocalCalendar; +import at.bitfire.davdroid.resource.LocalStorageException; + +public class LocalCalendarTest extends InstrumentationTestCase { + + private static final String + TAG = "davroid.LocalCalendarTest", + calendarName = "DAVdroid_Test"; + + ContentProviderClient providerClient; + Account testAccount = new Account(calendarName, CalendarContract.ACCOUNT_TYPE_LOCAL); + LocalCalendar testCalendar; + + + // helpers + + private Uri syncAdapterURI(Uri uri) { + return uri.buildUpon() + .appendQueryParameter(Calendars.ACCOUNT_NAME, calendarName) + .appendQueryParameter(Calendars.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL) + .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true"). + build(); + } + + private long insertNewEvent() throws LocalStorageException, RemoteException { + ContentValues values = new ContentValues(); + values.put(Events.CALENDAR_ID, testCalendar.getId()); + values.put(Events.TITLE, "Test Event"); + values.put(Events.ALL_DAY, 0); + values.put(Events.DTSTART, Calendar.getInstance().getTimeInMillis()); + values.put(Events.DTEND, Calendar.getInstance().getTimeInMillis()); + values.put(Events.EVENT_TIMEZONE, "UTC"); + values.put(Events.DIRTY, 1); + return ContentUris.parseId(providerClient.insert(syncAdapterURI(Events.CONTENT_URI), values)); + } + + private void deleteEvent(long id) throws RemoteException { + providerClient.delete(syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, id)), null, null); + } + + + // initialization + + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) + protected void setUp() throws Exception { + ContentResolver resolver = getInstrumentation().getContext().getContentResolver(); + providerClient = resolver.acquireContentProviderClient(CalendarContract.AUTHORITY); + + long id; + + @Cleanup Cursor cursor = providerClient.query(Calendars.CONTENT_URI, + new String[] { Calendars._ID }, + Calendars.ACCOUNT_TYPE + "=? AND " + Calendars.NAME + "=?", + new String[] { CalendarContract.ACCOUNT_TYPE_LOCAL, calendarName }, + null); + if (cursor.moveToNext()) { + // found local test calendar + id = cursor.getLong(0); + Log.d(TAG, "Found test calendar with ID " + id); + + } else { + // no local test calendar found, create + ContentValues values = new ContentValues(); + values.put(Calendars.ACCOUNT_NAME, testAccount.name); + values.put(Calendars.ACCOUNT_TYPE, testAccount.type); + values.put(Calendars.NAME, calendarName); + values.put(Calendars.CALENDAR_DISPLAY_NAME, calendarName); + values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_OWNER); + values.put(Calendars.ALLOWED_REMINDERS, Reminders.METHOD_ALERT); + values.put(Calendars.SYNC_EVENTS, 0); + values.put(Calendars.VISIBLE, 1); + + if (android.os.Build.VERSION.SDK_INT >= 15) { + values.put(Calendars.ALLOWED_AVAILABILITY, Events.AVAILABILITY_BUSY + "," + Events.AVAILABILITY_FREE + "," + Events.AVAILABILITY_TENTATIVE); + values.put(Calendars.ALLOWED_ATTENDEE_TYPES, Attendees.TYPE_NONE + "," + Attendees.TYPE_OPTIONAL + "," + Attendees.TYPE_REQUIRED + "," + Attendees.TYPE_RESOURCE); + } + + Uri calendarURI = providerClient.insert(syncAdapterURI(Calendars.CONTENT_URI), values); + + id = ContentUris.parseId(calendarURI); + Log.d(TAG, "Created test calendar with ID " + id); + } + + testCalendar = new LocalCalendar(testAccount, providerClient, id, null); + } + + protected void tearDown() throws Exception { + Uri uri = ContentUris.withAppendedId(syncAdapterURI(Calendars.CONTENT_URI), testCalendar.getId()); + providerClient.delete(uri, null, null); + } + + + // tests + + public void testCTags() throws LocalStorageException { + assertNull(testCalendar.getCTag()); + + final String cTag = "just-modified"; + testCalendar.setCTag(cTag); + + assertEquals(cTag, testCalendar.getCTag()); + } + + public void testFindNew() throws LocalStorageException, RemoteException { + // at the beginning, there are no dirty events + assertTrue(testCalendar.findNew().length == 0); + assertTrue(testCalendar.findUpdated().length == 0); + + // insert a "new" event + long id = insertNewEvent(); + try { + // there must be one "new" event now + assertTrue(testCalendar.findNew().length == 1); + assertTrue(testCalendar.findUpdated().length == 0); + + // nothing has changed, the record must still be "new" + // see issue #233 + assertTrue(testCalendar.findNew().length == 1); + assertTrue(testCalendar.findUpdated().length == 0); + } finally { + deleteEvent(id); + } + } + +} diff --git a/test/src/at/bitfire/davdroid/syncadapter/DavResourceFinderTest.java b/test/src/at/bitfire/davdroid/syncadapter/DavResourceFinderTest.java new file mode 100644 index 00000000..85f7e1e9 --- /dev/null +++ b/test/src/at/bitfire/davdroid/syncadapter/DavResourceFinderTest.java @@ -0,0 +1,70 @@ +package at.bitfire.davdroid.syncadapter; + +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.util.List; + +import android.test.InstrumentationTestCase; +import at.bitfire.davdroid.resource.DavResourceFinder; +import at.bitfire.davdroid.resource.ServerInfo; +import at.bitfire.davdroid.resource.ServerInfo.ResourceInfo; +import at.bitfire.davdroid.test.Constants; +import ezvcard.VCardVersion; + +public class DavResourceFinderTest extends InstrumentationTestCase { + + DavResourceFinder finder; + + @Override + protected void setUp() { + finder = new DavResourceFinder(getInstrumentation().getContext()); + } + + @Override + protected void tearDown() throws IOException { + finder.close(); + } + + + public void testFindResourcesRobohydra() throws Exception { + ServerInfo info = new ServerInfo(new URI(Constants.ROBOHYDRA_BASE), "test", "test", true); + finder.findResources(info); + + // CardDAV + assertTrue(info.isCardDAV()); + List collections = info.getAddressBooks(); + assertEquals(1, collections.size()); + + assertEquals("Default Address Book", collections.get(0).getDescription()); + assertEquals(VCardVersion.V4_0, collections.get(0).getVCardVersion()); + + // CalDAV + assertTrue(info.isCalDAV()); + collections = info.getCalendars(); + assertEquals(2, collections.size()); + + ResourceInfo resource = collections.get(0); + assertEquals("Private Calendar", resource.getTitle()); + assertEquals("This is my private calendar.", resource.getDescription()); + assertFalse(resource.isReadOnly()); + + resource = collections.get(1); + assertEquals("Work Calendar", resource.getTitle()); + assertTrue(resource.isReadOnly()); + } + + + public void testGetInitialContextURL() throws Exception { + // without SRV records, but with well-known paths + ServerInfo roboHydra = new ServerInfo(new URI(Constants.ROBOHYDRA_BASE), "test", "test", true); + assertEquals(new URL(Constants.roboHydra, "/"), finder.getInitialContextURL(roboHydra, "caldav")); + assertEquals(new URL(Constants.roboHydra, "/"), finder.getInitialContextURL(roboHydra, "carddav")); + + // with SRV records and well-known paths + ServerInfo iCloud = new ServerInfo(new URI("mailto:test@icloud.com"), "", "", true); + assertEquals(new URL("https://contacts.icloud.com/"), finder.getInitialContextURL(iCloud, "carddav")); + assertEquals(new URL("https://caldav.icloud.com/"), finder.getInitialContextURL(iCloud, "caldav")); + } + +} diff --git a/test/src/at/bitfire/davdroid/test/ArrayUtilsTest.java b/test/src/at/bitfire/davdroid/test/ArrayUtilsTest.java new file mode 100644 index 00000000..2761d2be --- /dev/null +++ b/test/src/at/bitfire/davdroid/test/ArrayUtilsTest.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.test; +import java.util.Arrays; + +import junit.framework.TestCase; +import at.bitfire.davdroid.ArrayUtils; + + +public class ArrayUtilsTest extends TestCase { + + public void testPartition() { + // n == 0 + assertTrue(Arrays.deepEquals( + new Long[0][0], + ArrayUtils.partition(new Long[] { }, 5))); + + // n < max + assertTrue(Arrays.deepEquals( + new Long[][] { { 1l, 2l } }, + ArrayUtils.partition(new Long[] { 1l, 2l }, 5))); + + // n == max + assertTrue(Arrays.deepEquals( + new Long[][] { { 1l, 2l }, { 3l, 4l } }, + ArrayUtils.partition(new Long[] { 1l, 2l, 3l, 4l }, 2))); + + // n > max + assertTrue(Arrays.deepEquals( + new Long[][] { { 1l, 2l, 3l, 4l, 5l }, { 6l, 7l, 8l, 9l, 10l }, { 11l } }, + ArrayUtils.partition(new Long[] { 1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l, 10l, 11l }, 5))); + } + +} diff --git a/test/src/at/bitfire/davdroid/test/Constants.java b/test/src/at/bitfire/davdroid/test/Constants.java new file mode 100644 index 00000000..707762b3 --- /dev/null +++ b/test/src/at/bitfire/davdroid/test/Constants.java @@ -0,0 +1,19 @@ +package at.bitfire.davdroid.test; + +import java.net.MalformedURLException; +import java.net.URL; + +import android.util.Log; + +public class Constants { + public static final String ROBOHYDRA_BASE = "http://10.0.0.11:3000/"; + + public static URL roboHydra; + static { + try { + roboHydra = new URL(ROBOHYDRA_BASE); + } catch(MalformedURLException e) { + Log.wtf("davdroid.test.Constants", "Invalid RoboHydra base URL"); + } + } +} diff --git a/test/src/at/bitfire/davdroid/test/ContactTest.java b/test/src/at/bitfire/davdroid/test/ContactTest.java new file mode 100644 index 00000000..a98ed78f --- /dev/null +++ b/test/src/at/bitfire/davdroid/test/ContactTest.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.test; + +import java.io.IOException; +import java.io.InputStream; + +import lombok.Cleanup; +import net.fortuna.ical4j.data.ParserException; +import android.content.res.AssetManager; +import android.test.InstrumentationTestCase; +import at.bitfire.davdroid.resource.Contact; +import ezvcard.property.Impp; + +public class ContactTest extends InstrumentationTestCase { + AssetManager assetMgr; + + public void setUp() { + assetMgr = getInstrumentation().getContext().getResources().getAssets(); + } + + public void testIMPP() throws IOException { + Contact c = parseVCard("impp.vcf"); + assertEquals("test mctest", c.getDisplayName()); + + Impp jabber = c.getImpps().get(0); + assertNull(jabber.getProtocol()); + assertEquals("test-without-valid-scheme@test.tld", jabber.getHandle()); + } + + + public void testParseVcard3() throws IOException, ParserException { + Contact c = parseVCard("vcard3-sample1.vcf"); + + assertEquals("Forrest Gump", c.getDisplayName()); + assertEquals("Forrest", c.getGivenName()); + assertEquals("Gump", c.getFamilyName()); + + assertEquals(2, c.getPhoneNumbers().size()); + assertEquals("(111) 555-1212", c.getPhoneNumbers().get(0).getText()); + + assertEquals(1, c.getEmails().size()); + assertEquals("forrestgump@example.com", c.getEmails().get(0).getValue()); + + assertFalse(c.isStarred()); + } + + + private Contact parseVCard(String fileName) throws IOException { + @Cleanup InputStream in = assetMgr.open(fileName, AssetManager.ACCESS_STREAMING); + + Contact c = new Contact(fileName, null); + c.parseEntity(in); + + return c; + } +} diff --git a/test/src/at/bitfire/davdroid/test/URLUtilsTest.java b/test/src/at/bitfire/davdroid/test/URLUtilsTest.java new file mode 100644 index 00000000..555b9b6b --- /dev/null +++ b/test/src/at/bitfire/davdroid/test/URLUtilsTest.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.test; + +import java.net.URL; + +import junit.framework.TestCase; +import at.bitfire.davdroid.URLUtils; + +public class URLUtilsTest extends TestCase { + + public void testEnsureTrailingSlash() throws Exception { + assertEquals("/test/", URLUtils.ensureTrailingSlash("/test")); + assertEquals("/test/", URLUtils.ensureTrailingSlash("/test/")); + + String withoutSlash = "http://www.test.at/dav/collection", + withSlash = withoutSlash + "/"; + assertEquals(new URL(withSlash), URLUtils.ensureTrailingSlash(new URL(withoutSlash))); + assertEquals(new URL(withSlash), URLUtils.ensureTrailingSlash(new URL(withSlash))); + } + + public void testSanitize() { + assertNull(URLUtils.sanitize(null)); + + // escape "@" + assertEquals("https://my%40server/my%40email.com/dir", URLUtils.sanitize("https://my@server/my@email.com/dir")); + assertEquals("http://my%40server/my%40email.com/dir", URLUtils.sanitize("http://my@server/my@email.com/dir")); + assertEquals("//my%40server/my%40email.com/dir", URLUtils.sanitize("//my@server/my@email.com/dir")); + assertEquals("/my%40email.com/dir", URLUtils.sanitize("/my@email.com/dir")); + assertEquals("my%40email.com/dir", URLUtils.sanitize("my@email.com/dir")); + + // escape ":" in path but not as port separator + assertEquals("https://www.test.at:80/my%3afile.vcf", URLUtils.sanitize("https://www.test.at:80/my:file.vcf")); + assertEquals("http://www.test.at:80/my%3afile.vcf", URLUtils.sanitize("http://www.test.at:80/my:file.vcf")); + assertEquals("//www.test.at:80/my%3afile.vcf", URLUtils.sanitize("//www.test.at:80/my:file.vcf")); + assertEquals("/my%3afile.vcf", URLUtils.sanitize("/my:file.vcf")); + assertEquals("my%3afile.vcf", URLUtils.sanitize("my:file.vcf")); + + // keep literal IPv6 addresses (only in host name) + assertEquals("https://[1:2::1]/", URLUtils.sanitize("https://[1:2::1]/")); + assertEquals("/%5b1%3a2%3a%3a1%5d/", URLUtils.sanitize("/[1:2::1]/")); + } +} diff --git a/test/src/at/bitfire/davdroid/webdav/DavRedirectStrategyTest.java b/test/src/at/bitfire/davdroid/webdav/DavRedirectStrategyTest.java new file mode 100644 index 00000000..7467175f --- /dev/null +++ b/test/src/at/bitfire/davdroid/webdav/DavRedirectStrategyTest.java @@ -0,0 +1,74 @@ +package at.bitfire.davdroid.webdav; + +import java.io.IOException; +import java.net.URL; + +import junit.framework.TestCase; +import at.bitfire.davdroid.test.Constants; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.client.methods.HttpOptions; +import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient; +import ch.boye.httpclientandroidlib.impl.client.HttpClientBuilder; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +public class DavRedirectStrategyTest extends TestCase { + + CloseableHttpClient httpClient; + DavRedirectStrategy strategy = DavRedirectStrategy.INSTANCE; + + @Override + protected void setUp() { + httpClient = HttpClientBuilder.create() + .useSystemProperties() + .disableRedirectHandling() + .build(); + } + + @Override + protected void tearDown() throws IOException { + httpClient.close(); + } + + + // happy cases + + public void testNonRedirection() throws Exception { + HttpUriRequest request = new HttpOptions(Constants.roboHydra.toURI()); + HttpResponse response = httpClient.execute(request); + assertFalse(strategy.isRedirected(request, response, null)); + } + + public void testDefaultRedirection() throws Exception { + final String newLocation = "/new-location"; + + HttpContext context = HttpClientContext.create(); + HttpUriRequest request = new HttpOptions(new URL(Constants.roboHydra, "redirect/301?to=" + newLocation).toURI()); + HttpResponse response = httpClient.execute(request, context); + assertTrue(strategy.isRedirected(request, response, context)); + + HttpUriRequest redirected = strategy.getRedirect(request, response, context); + assertEquals(new URL(Constants.roboHydra, newLocation).toURI(), redirected.getURI()); + } + + + // error cases + + public void testMissingLocation() throws Exception { + HttpContext context = HttpClientContext.create(); + HttpUriRequest request = new HttpOptions(new URL(Constants.roboHydra, "redirect/without-location").toURI()); + HttpResponse response = httpClient.execute(request, context); + assertFalse(strategy.isRedirected(request, response, context)); + } + + public void testRelativeLocation() throws Exception { + HttpContext context = HttpClientContext.create(); + HttpUriRequest request = new HttpOptions(new URL(Constants.roboHydra, "redirect/relative").toURI()); + HttpResponse response = httpClient.execute(request, context); + assertTrue(strategy.isRedirected(request, response, context)); + + HttpUriRequest redirected = strategy.getRedirect(request, response, context); + assertEquals(new URL(Constants.roboHydra, "/new/location").toURI(), redirected.getURI()); + } +} diff --git a/test/src/at/bitfire/davdroid/webdav/TlsSniSocketFactoryTest.java b/test/src/at/bitfire/davdroid/webdav/TlsSniSocketFactoryTest.java new file mode 100644 index 00000000..f00844d5 --- /dev/null +++ b/test/src/at/bitfire/davdroid/webdav/TlsSniSocketFactoryTest.java @@ -0,0 +1,27 @@ +package at.bitfire.davdroid.webdav; + +import java.io.IOException; + +import javax.net.ssl.SSLSocket; + +import android.util.Log; +import junit.framework.TestCase; + +public class TlsSniSocketFactoryTest extends TestCase { + private static final String TAG = "davdroid.TlsSniSocketFactoryTest"; + + public void testCiphers() throws IOException { + SSLSocket socket = (SSLSocket)TlsSniSocketFactory.INSTANCE.createSocket(null); + + Log.i(TAG, "Enabled:"); + for (String cipher : socket.getEnabledCipherSuites()) + Log.i(TAG, cipher); + + Log.i(TAG, "Supported:"); + for (String cipher : socket.getSupportedCipherSuites()) + Log.i(TAG, cipher); + + assert(true); + } + +} diff --git a/test/src/at/bitfire/davdroid/webdav/WebDavResourceTest.java b/test/src/at/bitfire/davdroid/webdav/WebDavResourceTest.java new file mode 100644 index 00000000..4a5b0510 --- /dev/null +++ b/test/src/at/bitfire/davdroid/webdav/WebDavResourceTest.java @@ -0,0 +1,234 @@ +/******************************************************************************* + * Copyright (c) 2014 Ricki Hirner (bitfire web engineering). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + ******************************************************************************/ +package at.bitfire.davdroid.webdav; + +import java.io.InputStream; +import java.net.URL; +import java.util.Arrays; +import java.util.List; + +import javax.net.ssl.SSLPeerUnverifiedException; + +import lombok.Cleanup; + +import org.apache.commons.io.IOUtils; + +import android.content.res.AssetManager; +import android.test.InstrumentationTestCase; +import at.bitfire.davdroid.test.Constants; +import at.bitfire.davdroid.webdav.HttpPropfind.Mode; +import at.bitfire.davdroid.webdav.WebDavResource.PutMode; +import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient; + +// tests require running robohydra! + +public class WebDavResourceTest extends InstrumentationTestCase { + static byte[] SAMPLE_CONTENT = new byte[] { 1, 2, 3, 4, 5 }; + + final static String PATH_SIMPLE_FILE = "collection/new.file"; + + AssetManager assetMgr; + CloseableHttpClient httpClient; + + WebDavResource baseDAV; + WebDavResource simpleFile, + davCollection, davNonExistingFile, davExistingFile, + davInvalid; + + @Override + protected void setUp() throws Exception { + httpClient = DavHttpClient.create(true, true); + + assetMgr = getInstrumentation().getContext().getResources().getAssets(); + + baseDAV = new WebDavResource(httpClient, new URL(Constants.roboHydra, "/dav/")); + + simpleFile = new WebDavResource(httpClient, new URL(Constants.ROBOHYDRA_BASE + "assets/test.random")); + + davCollection = new WebDavResource(httpClient, new URL(Constants.ROBOHYDRA_BASE + "dav/")); + davNonExistingFile = new WebDavResource(davCollection, "collection/new.file"); + davExistingFile = new WebDavResource(davCollection, "collection/existing.file"); + + davInvalid = new WebDavResource(httpClient, new URL(Constants.ROBOHYDRA_BASE + "dav-invalid/")); + } + + @Override + protected void tearDown() throws Exception { + httpClient.close(); + } + + + /* test feature detection */ + + public void testOptions() throws Exception { + String[] davMethods = new String[] { "PROPFIND", "GET", "PUT", "DELETE", "REPORT" }, + davCapabilities = new String[] { "addressbook", "calendar-access" }; + + WebDavResource capable = new WebDavResource(baseDAV); + capable.options(); + for (String davMethod : davMethods) + assert(capable.supportsMethod(davMethod)); + for (String capability : davCapabilities) + assert(capable.supportsDAV(capability)); + } + + public void testPropfindCurrentUserPrincipal() throws Exception { + davCollection.propfind(HttpPropfind.Mode.CURRENT_USER_PRINCIPAL); + assertEquals("/dav/principals/users/test", davCollection.getCurrentUserPrincipal()); + + try { + simpleFile.propfind(HttpPropfind.Mode.CURRENT_USER_PRINCIPAL); + fail(); + + } catch(DavException ex) { + } + assertNull(simpleFile.getCurrentUserPrincipal()); + } + + public void testPropfindHomeSets() throws Exception { + WebDavResource dav = new WebDavResource(davCollection, "principals/users/test"); + dav.propfind(HttpPropfind.Mode.HOME_SETS); + assertEquals("/dav/addressbooks/test/", dav.getAddressbookHomeSet()); + assertEquals("/dav/calendars/test/", dav.getCalendarHomeSet()); + } + + public void testPropfindAddressBooks() throws Exception { + WebDavResource dav = new WebDavResource(davCollection, "addressbooks/test"); + dav.propfind(HttpPropfind.Mode.CARDDAV_COLLECTIONS); + assertEquals(2, dav.getMembers().size()); + for (WebDavResource member : dav.getMembers()) { + if (member.getName().equals("default-v4.vcf")) + assertTrue(member.isAddressBook()); + else + assertFalse(member.isAddressBook()); + assertFalse(member.isCalendar()); + } + } + + public void testPropfindCalendars() throws Exception { + WebDavResource dav = new WebDavResource(davCollection, "calendars/test"); + dav.propfind(Mode.CALDAV_COLLECTIONS); + assertEquals(3, dav.getMembers().size()); + assertEquals("0xFF00FF", dav.getMembers().get(2).getColor()); + for (WebDavResource member : dav.getMembers()) { + if (member.getName().contains(".ics")) + assertTrue(member.isCalendar()); + else + assertFalse(member.isCalendar()); + assertFalse(member.isAddressBook()); + } + } + + public void testPropfindTrailingSlashes() throws Exception { + final String principalOK = "/principals/ok"; + + String requestPaths[] = { + "/dav/collection-response-with-trailing-slash", + "/dav/collection-response-with-trailing-slash/", + "/dav/collection-response-without-trailing-slash", + "/dav/collection-response-without-trailing-slash/" + }; + + for (String path : requestPaths) { + WebDavResource davSlash = new WebDavResource(davCollection, path); + davSlash.propfind(Mode.CARDDAV_COLLECTIONS); + assertEquals(principalOK, davSlash.getCurrentUserPrincipal()); + } + } + + + /* test normal HTTP/WebDAV */ + + public void testPropfindRedirection() throws Exception { + // PROPFIND redirection + WebDavResource redirected = new WebDavResource(baseDAV, "/redirect/301?to=/dav/"); + redirected.propfind(Mode.CURRENT_USER_PRINCIPAL); + assertEquals("/dav/", redirected.getLocation().getPath()); + } + + public void testGet() throws Exception { + simpleFile.get("*/*"); + @Cleanup InputStream is = assetMgr.open("test.random", AssetManager.ACCESS_STREAMING); + byte[] expected = IOUtils.toByteArray(is); + assertTrue(Arrays.equals(expected, simpleFile.getContent())); + } + + public void testGetHttpsWithSni() throws Exception { + WebDavResource file = new WebDavResource(httpClient, new URL("https://sni.velox.ch")); + + boolean sniWorking = false; + try { + file.get("*/*"); + sniWorking = true; + } catch (SSLPeerUnverifiedException e) { + } + + assertTrue(sniWorking); + } + + public void testMultiGet() throws Exception { + WebDavResource davAddressBook = new WebDavResource(davCollection, "addressbooks/default.vcf"); + davAddressBook.multiGet(DavMultiget.Type.ADDRESS_BOOK, new String[] { "1.vcf", "2.vcf" }); + assertEquals(2, davAddressBook.getMembers().size()); + for (WebDavResource member : davAddressBook.getMembers()) { + assertNotNull(member.getContent()); + } + } + + public void testPutAddDontOverwrite() throws Exception { + // should succeed on a non-existing file + assertEquals("has-just-been-created", davNonExistingFile.put(SAMPLE_CONTENT, PutMode.ADD_DONT_OVERWRITE)); + + // should fail on an existing file + try { + davExistingFile.put(SAMPLE_CONTENT, PutMode.ADD_DONT_OVERWRITE); + fail(); + } catch(PreconditionFailedException ex) { + } + } + + public void testPutUpdateDontOverwrite() throws Exception { + // should succeed on an existing file + assertEquals("has-just-been-updated", davExistingFile.put(SAMPLE_CONTENT, PutMode.UPDATE_DONT_OVERWRITE)); + + // should fail on a non-existing file + try { + davNonExistingFile.put(SAMPLE_CONTENT, PutMode.UPDATE_DONT_OVERWRITE); + fail(); + } catch(PreconditionFailedException ex) { + } + } + + public void testDelete() throws Exception { + // should succeed on an existing file + davExistingFile.delete(); + + // should fail on a non-existing file + try { + davNonExistingFile.delete(); + fail(); + } catch (NotFoundException e) { + } + } + + + /* test CalDAV/CardDAV */ + + + /* special test */ + + public void testInvalidURLs() throws Exception { + WebDavResource dav = new WebDavResource(davInvalid, "addressbooks/user%40domain/"); + dav.propfind(HttpPropfind.Mode.CARDDAV_COLLECTIONS); + List members = dav.getMembers(); + assertEquals(2, members.size()); + assertEquals(Constants.ROBOHYDRA_BASE + "dav/addressbooks/user%40domain/My%20Contacts%3a1.vcf/", members.get(0).getLocation().toString()); + assertEquals("https://example.com/user%40domain/absolute-url.vcf/", members.get(1).getLocation().toString()); + } + +}